Comparing version 1.1.3 to 1.2.0
NoFlo ChangeLog | ||
=============== | ||
## 1.2.0 (August 28th 2020) | ||
* Ported NoFlo from CoffeeScript to ES6 | ||
* Deprecated constructing networks with `new noflo.Network`. Use `noflo.createNetwork` instead, with the following options available: | ||
- `subscribeGraph: true`: Uses `LegacyNetwork` which modifies network topology based on changes in graph. This can cause some types of errors to be silent. | ||
- `subscribeGraph: false`: Uses `Network`: network topology can be changed with network's methods (`addNode`, `removeEdge`, etc) and will be also written to the graph. | ||
For backwards compatibility reasons, `subscribeGraph` defaults to `true`. Adapt your applications to use `false` instead and start utilizing Network methods for any changes to a running graph. | ||
* Added support for a more standard `noflo.createNetwork(graph, options, callback)` signature, with backwards compatibility for the legacy `noflo.createNetwork(graph, callback, options)` signature | ||
* Removed support for `noflo.WirePattern`. WirePattern has been deprecated since 1.0, and all code using it should be migrated to the latest Process API | ||
* Removed support for changing component icon and description statically (on class level) at run-time (i.e. `ComponentName::icon = 'new-icon'`). Component icon and description should be set in class constructor or in `getComponent` instead. Changing icon and description for a specific instance (process) is not affected and is fully supported | ||
* Added optional `networkCallback` option for `noflo.asCallback` to provide access to the network instance for debugging purposes | ||
## 1.1.3 (April 12th 2018) | ||
@@ -5,0 +17,0 @@ |
@@ -0,1 +1,25 @@ | ||
"use strict"; | ||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } | ||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } | ||
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } | ||
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } | ||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } | ||
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } | ||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } | ||
// NoFlo - Flow-Based Programming for JavaScript | ||
@@ -6,20 +30,30 @@ // (c) 2013-2017 Flowhub UG | ||
// The Graph component is used to wrap NoFlo Networks into components inside | ||
/* eslint-disable | ||
class-methods-use-this, | ||
import/no-unresolved, | ||
*/ | ||
var noflo = require('../lib/NoFlo'); // The Graph component is used to wrap NoFlo Networks into components inside | ||
// another network. | ||
var Graph, noflo; | ||
noflo = require("../lib/NoFlo"); | ||
Graph = class Graph extends noflo.Component { | ||
constructor(metadata) { | ||
super(); | ||
this.metadata = metadata; | ||
this.network = null; | ||
this.ready = true; | ||
this.started = false; | ||
this.starting = false; | ||
this.baseDir = null; | ||
this.loader = null; | ||
this.load = 0; | ||
this.inPorts = new noflo.InPorts({ | ||
var Graph = /*#__PURE__*/function (_noflo$Component) { | ||
_inherits(Graph, _noflo$Component); | ||
var _super = _createSuper(Graph); | ||
function Graph(metadata) { | ||
var _this; | ||
_classCallCheck(this, Graph); | ||
_this = _super.call(this); | ||
_this.metadata = metadata; | ||
_this.network = null; | ||
_this.ready = true; | ||
_this.started = false; | ||
_this.starting = false; | ||
_this.baseDir = null; | ||
_this.loader = null; | ||
_this.load = 0; | ||
_this.inPorts = new noflo.InPorts({ | ||
graph: { | ||
@@ -31,231 +65,315 @@ datatype: 'all', | ||
}); | ||
this.outPorts = new noflo.OutPorts; | ||
this.inPorts.graph.on('ip', (packet) => { | ||
_this.outPorts = new noflo.OutPorts(); | ||
_this.inPorts.graph.on('ip', function (packet) { | ||
if (packet.type !== 'data') { | ||
return; | ||
} | ||
return this.setGraph(packet.data, (err) => { | ||
_this.setGraph(packet.data, function (err) { | ||
// TODO: Port this part to Process API and use output.error method instead | ||
if (err) { | ||
// TODO: Port this part to Process API and use output.error method instead | ||
return this.error(err); | ||
_this.error(err); | ||
} | ||
}); | ||
}); | ||
return _this; | ||
} | ||
setGraph(graph, callback) { | ||
this.ready = false; | ||
if (typeof graph === 'object') { | ||
if (typeof graph.addNode === 'function') { | ||
// Existing Graph object | ||
this.createNetwork(graph, callback); | ||
_createClass(Graph, [{ | ||
key: "setGraph", | ||
value: function setGraph(graph, callback) { | ||
var _this2 = this; | ||
this.ready = false; | ||
if (_typeof(graph) === 'object') { | ||
if (typeof graph.addNode === 'function') { | ||
// Existing Graph object | ||
this.createNetwork(graph, callback); | ||
return; | ||
} // JSON definition of a graph | ||
noflo.graph.loadJSON(graph, function (err, instance) { | ||
var inst = instance; | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
inst.baseDir = _this2.baseDir; | ||
_this2.createNetwork(inst, callback); | ||
}); | ||
return; | ||
} | ||
// JSON definition of a graph | ||
noflo.graph.loadJSON(graph, (err, instance) => { | ||
var graphName = graph; | ||
if (graphName.substr(0, 1) !== '/' && graphName.substr(1, 1) !== ':' && process && process.cwd) { | ||
graphName = "".concat(process.cwd(), "/").concat(graphName); | ||
} | ||
noflo.graph.loadFile(graphName, function (err, instance) { | ||
var inst = instance; | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
instance.baseDir = this.baseDir; | ||
return this.createNetwork(instance, callback); | ||
inst.baseDir = _this2.baseDir; | ||
_this2.createNetwork(inst, callback); | ||
}); | ||
return; | ||
} | ||
if (graph.substr(0, 1) !== "/" && graph.substr(1, 1) !== ":" && process && process.cwd) { | ||
graph = `${process.cwd()}/${graph}`; | ||
} | ||
return noflo.graph.loadFile(graph, (err, instance) => { | ||
if (err) { | ||
return callback(err); | ||
}, { | ||
key: "createNetwork", | ||
value: function createNetwork(graph, callback) { | ||
var _this3 = this; | ||
this.description = graph.properties.description || ''; | ||
this.icon = graph.properties.icon || this.icon; | ||
var graphObj = graph; | ||
if (!graphObj.name) { | ||
graphObj.name = this.nodeId; | ||
} | ||
instance.baseDir = this.baseDir; | ||
return this.createNetwork(instance, callback); | ||
}); | ||
} | ||
createNetwork(graph, callback) { | ||
this.description = graph.properties.description || ''; | ||
this.icon = graph.properties.icon || this.icon; | ||
if (!graph.name) { | ||
graph.name = this.nodeId; | ||
} | ||
graph.componentLoader = this.loader; | ||
return noflo.createNetwork(graph, (err, network1) => { | ||
this.network = network1; | ||
if (err) { | ||
return callback(err); | ||
} | ||
this.emit('network', this.network); | ||
// Subscribe to network lifecycle | ||
this.subscribeNetwork(this.network); | ||
// Wire the network up | ||
return this.network.connect((err) => { | ||
var name, node, ref; | ||
graphObj.componentLoader = this.loader; | ||
noflo.createNetwork(graphObj, { | ||
delay: true, | ||
subscribeGraph: false | ||
}, function (err, network) { | ||
_this3.network = network; | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
ref = this.network.processes; | ||
for (name in ref) { | ||
node = ref[name]; | ||
// Map exported ports to local component | ||
this.findEdgePorts(name, node); | ||
_this3.emit('network', _this3.network); // Subscribe to network lifecycle | ||
_this3.subscribeNetwork(_this3.network); // Wire the network up | ||
_this3.network.connect(function (err2) { | ||
if (err2) { | ||
callback(err2); | ||
return; | ||
} | ||
Object.keys(_this3.network.processes).forEach(function (name) { | ||
// Map exported ports to local component | ||
var node = _this3.network.processes[name]; | ||
_this3.findEdgePorts(name, node); | ||
}); // Finally set ourselves as "ready" | ||
_this3.setToReady(); | ||
callback(); | ||
}); | ||
}); | ||
} | ||
}, { | ||
key: "subscribeNetwork", | ||
value: function subscribeNetwork() { | ||
var _this4 = this; | ||
var contexts = []; | ||
this.network.on('start', function () { | ||
var ctx = {}; | ||
contexts.push(ctx); | ||
return _this4.activate(ctx); | ||
}); | ||
return this.network.on('end', function () { | ||
var ctx = contexts.pop(); | ||
if (!ctx) { | ||
return; | ||
} | ||
// Finally set ourselves as "ready" | ||
this.setToReady(); | ||
return callback(); | ||
_this4.deactivate(ctx); | ||
}); | ||
}, true); | ||
} | ||
} | ||
}, { | ||
key: "isExportedInport", | ||
value: function isExportedInport(port, nodeName, portName) { | ||
// First we check disambiguated exported ports | ||
var keys = Object.keys(this.network.graph.inports); | ||
subscribeNetwork(network) { | ||
var contexts; | ||
contexts = []; | ||
this.network.on('start', () => { | ||
var ctx; | ||
ctx = {}; | ||
contexts.push(ctx); | ||
return this.activate(ctx); | ||
}); | ||
return this.network.on('end', () => { | ||
var ctx; | ||
ctx = contexts.pop(); | ||
if (!ctx) { | ||
return; | ||
} | ||
return this.deactivate(ctx); | ||
}); | ||
} | ||
for (var i = 0; i < keys.length; i += 1) { | ||
var pub = keys[i]; | ||
var priv = this.network.graph.inports[pub]; | ||
isExportedInport(port, nodeName, portName) { | ||
var priv, pub, ref; | ||
ref = this.network.graph.inports; | ||
// First we check disambiguated exported ports | ||
for (pub in ref) { | ||
priv = ref[pub]; | ||
if (!(priv.process === nodeName && priv.port === portName)) { | ||
continue; | ||
} | ||
return pub; | ||
if (priv.process === nodeName && priv.port === portName) { | ||
return pub; | ||
} | ||
} // Component has exported ports and this isn't one of them | ||
return false; | ||
} | ||
// Component has exported ports and this isn't one of them | ||
return false; | ||
} | ||
}, { | ||
key: "isExportedOutport", | ||
value: function isExportedOutport(port, nodeName, portName) { | ||
// First we check disambiguated exported ports | ||
var keys = Object.keys(this.network.graph.outports); | ||
isExportedOutport(port, nodeName, portName) { | ||
var priv, pub, ref; | ||
ref = this.network.graph.outports; | ||
// First we check disambiguated exported ports | ||
for (pub in ref) { | ||
priv = ref[pub]; | ||
if (!(priv.process === nodeName && priv.port === portName)) { | ||
continue; | ||
} | ||
return pub; | ||
for (var i = 0; i < keys.length; i += 1) { | ||
var pub = keys[i]; | ||
var priv = this.network.graph.outports[pub]; | ||
if (priv.process === nodeName && priv.port === portName) { | ||
return pub; | ||
} | ||
} // Component has exported ports and this isn't one of them | ||
return false; | ||
} | ||
// Component has exported ports and this isn't one of them | ||
return false; | ||
} | ||
}, { | ||
key: "setToReady", | ||
value: function setToReady() { | ||
var _this5 = this; | ||
setToReady() { | ||
if (typeof process !== 'undefined' && process.execPath && process.execPath.indexOf('node') !== -1) { | ||
return process.nextTick(() => { | ||
this.ready = true; | ||
return this.emit('ready'); | ||
}); | ||
} else { | ||
return setTimeout(() => { | ||
this.ready = true; | ||
return this.emit('ready'); | ||
}, 0); | ||
if (typeof process !== 'undefined' && process.execPath && process.execPath.indexOf('node') !== -1) { | ||
process.nextTick(function () { | ||
_this5.ready = true; | ||
return _this5.emit('ready'); | ||
}); | ||
} else { | ||
setTimeout(function () { | ||
_this5.ready = true; | ||
return _this5.emit('ready'); | ||
}, 0); | ||
} | ||
} | ||
} | ||
}, { | ||
key: "findEdgePorts", | ||
value: function findEdgePorts(name, process) { | ||
var _this6 = this; | ||
findEdgePorts(name, process) { | ||
var inPorts, outPorts, port, portName, targetPortName; | ||
inPorts = process.component.inPorts.ports; | ||
outPorts = process.component.outPorts.ports; | ||
for (portName in inPorts) { | ||
port = inPorts[portName]; | ||
targetPortName = this.isExportedInport(port, name, portName); | ||
if (targetPortName === false) { | ||
continue; | ||
} | ||
this.inPorts.add(targetPortName, port); | ||
this.inPorts[targetPortName].on('connect', () => { | ||
// Start the network implicitly if we're starting to get data | ||
if (this.starting) { | ||
var inPorts = process.component.inPorts.ports; | ||
var outPorts = process.component.outPorts.ports; | ||
Object.keys(inPorts).forEach(function (portName) { | ||
var port = inPorts[portName]; | ||
var targetPortName = _this6.isExportedInport(port, name, portName); | ||
if (targetPortName === false) { | ||
return; | ||
} | ||
if (this.network.isStarted()) { | ||
_this6.inPorts.add(targetPortName, port); | ||
_this6.inPorts[targetPortName].on('connect', function () { | ||
// Start the network implicitly if we're starting to get data | ||
if (_this6.starting) { | ||
return; | ||
} | ||
if (_this6.network.isStarted()) { | ||
return; | ||
} | ||
if (_this6.network.startupDate) { | ||
// Network was started, but did finish. Re-start simply | ||
_this6.network.setStarted(true); | ||
return; | ||
} // Network was never started, start properly | ||
_this6.setUp(function () {}); | ||
}); | ||
}); | ||
Object.keys(outPorts).forEach(function (portName) { | ||
var port = outPorts[portName]; | ||
var targetPortName = _this6.isExportedOutport(port, name, portName); | ||
if (targetPortName === false) { | ||
return; | ||
} | ||
if (this.network.startupDate) { | ||
// Network was started, but did finish. Re-start simply | ||
this.network.setStarted(true); | ||
return; | ||
} | ||
// Network was never started, start properly | ||
return this.setUp(function() {}); | ||
_this6.outPorts.add(targetPortName, port); | ||
}); | ||
return true; | ||
} | ||
for (portName in outPorts) { | ||
port = outPorts[portName]; | ||
targetPortName = this.isExportedOutport(port, name, portName); | ||
if (targetPortName === false) { | ||
continue; | ||
} | ||
this.outPorts.add(targetPortName, port); | ||
}, { | ||
key: "isReady", | ||
value: function isReady() { | ||
return this.ready; | ||
} | ||
return true; | ||
} | ||
}, { | ||
key: "isSubgraph", | ||
value: function isSubgraph() { | ||
return true; | ||
} | ||
}, { | ||
key: "isLegacy", | ||
value: function isLegacy() { | ||
return false; | ||
} | ||
}, { | ||
key: "setUp", | ||
value: function setUp(callback) { | ||
var _this7 = this; | ||
isReady() { | ||
return this.ready; | ||
} | ||
this.starting = true; | ||
isSubgraph() { | ||
return true; | ||
} | ||
if (!this.isReady()) { | ||
this.once('ready', function () { | ||
_this7.setUp(callback); | ||
}); | ||
return; | ||
} | ||
isLegacy() { | ||
return false; | ||
} | ||
if (!this.network) { | ||
callback(null); | ||
return; | ||
} | ||
setUp(callback) { | ||
this.starting = true; | ||
if (!this.isReady()) { | ||
this.once('ready', () => { | ||
return this.setUp(callback); | ||
this.network.start(function (err) { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
_this7.starting = false; | ||
callback(); | ||
}); | ||
return; | ||
} | ||
if (!this.network) { | ||
return callback(null); | ||
} | ||
return this.network.start((err) => { | ||
if (err) { | ||
return callback(err); | ||
} | ||
}, { | ||
key: "tearDown", | ||
value: function tearDown(callback) { | ||
this.starting = false; | ||
return callback(); | ||
}); | ||
} | ||
tearDown(callback) { | ||
this.starting = false; | ||
if (!this.network) { | ||
return callback(null); | ||
} | ||
return this.network.stop(function(err) { | ||
if (err) { | ||
return callback(err); | ||
if (!this.network) { | ||
callback(null); | ||
return; | ||
} | ||
return callback(); | ||
}); | ||
} | ||
}; | ||
this.network.stop(function (err) { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
exports.getComponent = function(metadata) { | ||
callback(); | ||
}); | ||
} | ||
}]); | ||
return Graph; | ||
}(noflo.Component); | ||
exports.getComponent = function (metadata) { | ||
return new Graph(metadata); | ||
}; | ||
}; |
@@ -0,25 +1,32 @@ | ||
"use strict"; | ||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
// NoFlo - Flow-Based Programming for JavaScript | ||
// (c) 2017-2018 Flowhub UG | ||
// NoFlo may be freely distributed under the MIT license | ||
var ComponentLoader, Graph, IP, Network, getType, internalSocket, normalizeOptions, normalizeOutput, prepareInputMap, prepareNetwork, runNetwork, sendOutputMap; | ||
ComponentLoader = require('./ComponentLoader').ComponentLoader; | ||
/* eslint-disable | ||
no-param-reassign, | ||
*/ | ||
var _require = require('fbp-graph'), | ||
Graph = _require.Graph; | ||
Network = require('./Network').Network; | ||
var _require2 = require('./ComponentLoader'), | ||
ComponentLoader = _require2.ComponentLoader; | ||
IP = require('./IP'); | ||
var _require3 = require('./Network'), | ||
Network = _require3.Network; | ||
internalSocket = require('./InternalSocket'); | ||
var IP = require('./IP'); | ||
Graph = require('fbp-graph').Graph; | ||
// ## asCallback embedding API | ||
var internalSocket = require('./InternalSocket'); // ## asCallback embedding API | ||
// | ||
// asCallback is a helper for embedding NoFlo components or | ||
// graphs in other JavaScript programs. | ||
// | ||
// By using the `noflo.asCallback` function, you can turn any | ||
// NoFlo component or NoFlo Graph instance into a regular, | ||
// Node.js-style JavaScript function. | ||
// | ||
// Each call to that function starts a new NoFlo network where | ||
@@ -29,8 +36,7 @@ // the given input arguments are sent as IP objects to matching | ||
// from the network will be sent to the callback function. | ||
// | ||
// If there was anything sent to an `error` outport, this will | ||
// be provided as the error argument to the callback. | ||
// ### Option normalization | ||
// | ||
// Here we handle the input valus given to the `asCallback` | ||
@@ -40,101 +46,111 @@ // function. This allows passing things like a pre-initialized | ||
// baseDir context. | ||
normalizeOptions = function(options, component) { | ||
function normalizeOptions(options, component) { | ||
if (!options) { | ||
options = {}; | ||
} | ||
if (!options.name) { | ||
options.name = component; | ||
} | ||
if (options.loader) { | ||
options.baseDir = options.loader.baseDir; | ||
} | ||
if (!options.baseDir && process && process.cwd) { | ||
options.baseDir = process.cwd(); | ||
} | ||
if (!options.loader) { | ||
options.loader = new ComponentLoader(options.baseDir); | ||
} | ||
if (!options.raw) { | ||
options.raw = false; | ||
} | ||
return options; | ||
}; | ||
// ### Network preparation | ||
} // ### Network preparation | ||
// | ||
// Each invocation of the asCallback-wrapped NoFlo graph | ||
// creates a new network. This way we can isolate multiple | ||
// executions of the function in their own contexts. | ||
prepareNetwork = function(component, options, callback) { | ||
function prepareNetwork(component, options, callback) { | ||
// If we were given a graph instance, then just create a network | ||
var network; | ||
// If we were given a graph instance, then just create a network | ||
if (typeof component === 'object') { | ||
if (_typeof(component) === 'object') { | ||
component.componentLoader = options.loader; | ||
network = new Network(component, options); | ||
// Wire the network up | ||
network.connect(function(err) { | ||
network = new Network(component, options); // Wire the network up | ||
network.connect(function (err) { | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
return callback(null, network); | ||
callback(null, network); | ||
}); | ||
return; | ||
} | ||
// Start by loading the component | ||
return options.loader.load(component, function(err, instance) { | ||
var def, graph, inPorts, nodeName, outPorts, port; | ||
} // Start by loading the component | ||
options.loader.load(component, function (err, instance) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
// Prepare a graph wrapping the component | ||
graph = new Graph(options.name); | ||
nodeName = options.name; | ||
graph.addNode(nodeName, component); | ||
// Expose ports | ||
inPorts = instance.inPorts.ports; | ||
outPorts = instance.outPorts.ports; | ||
for (port in inPorts) { | ||
def = inPorts[port]; | ||
callback(err); | ||
return; | ||
} // Prepare a graph wrapping the component | ||
var graph = new Graph(options.name); | ||
var nodeName = options.name; | ||
graph.addNode(nodeName, component); // Expose ports | ||
var inPorts = instance.inPorts.ports; | ||
var outPorts = instance.outPorts.ports; | ||
Object.keys(inPorts).forEach(function (port) { | ||
graph.addInport(port, nodeName, port); | ||
} | ||
for (port in outPorts) { | ||
def = outPorts[port]; | ||
}); | ||
Object.keys(outPorts).forEach(function (port) { | ||
graph.addOutport(port, nodeName, port); | ||
} | ||
// Prepare network | ||
}); // Prepare network | ||
graph.componentLoader = options.loader; | ||
network = new Network(graph, options); | ||
// Wire the network up and start execution | ||
return network.connect(function(err) { | ||
if (err) { | ||
return callback(err); | ||
network = new Network(graph, options); // Wire the network up and start execution | ||
network.connect(function (err2) { | ||
if (err2) { | ||
callback(err2); | ||
return; | ||
} | ||
return callback(null, network); | ||
callback(null, network); | ||
}); | ||
}); | ||
}; | ||
// ### Network execution | ||
} // ### Network execution | ||
// | ||
// Once network is ready, we connect to all of its exported | ||
// in and outports and start the network. | ||
// | ||
// Input data is sent to the inports, and we collect IP | ||
// packets received on the outports. | ||
// | ||
// Once the network finishes, we send the resulting IP | ||
// objects to the callback. | ||
runNetwork = function(network, inputs, options, callback) { | ||
var inPorts, inSockets, outPorts, outSockets, received; | ||
function runNetwork(network, inputs, options, callback) { | ||
// Prepare inports | ||
inPorts = Object.keys(network.graph.inports); | ||
inSockets = {}; | ||
// Subscribe outports | ||
received = []; | ||
outPorts = Object.keys(network.graph.outports); | ||
outSockets = {}; | ||
outPorts.forEach(function(outport) { | ||
var portDef, process; | ||
portDef = network.graph.outports[outport]; | ||
process = network.getNode(portDef.process); | ||
var inSockets = {}; // Subscribe outports | ||
var received = []; | ||
var outPorts = Object.keys(network.graph.outports); | ||
var outSockets = {}; | ||
outPorts.forEach(function (outport) { | ||
var portDef = network.graph.outports[outport]; | ||
var process = network.getNode(portDef.process); | ||
outSockets[outport] = internalSocket.createSocket(); | ||
@@ -146,78 +162,104 @@ process.component.outPorts[portDef.port].attach(outSockets[outport]); | ||
}; | ||
return outSockets[outport].on('ip', function(ip) { | ||
var res; | ||
res = {}; | ||
outSockets[outport].on('ip', function (ip) { | ||
var res = {}; | ||
res[outport] = ip; | ||
return received.push(res); | ||
received.push(res); | ||
}); | ||
}); | ||
// Subscribe network finish | ||
network.once('end', function() { | ||
var port, socket; | ||
// Clear listeners | ||
for (port in outSockets) { | ||
socket = outSockets[port]; | ||
}); // Subscribe to process errors | ||
var onEnd = null; | ||
var onError = null; | ||
onError = function onError(err) { | ||
callback(err.error); | ||
network.removeListener('end', onEnd); | ||
}; | ||
network.once('process-error', onError); // Subscribe network finish | ||
onEnd = function onEnd() { | ||
// Clear listeners | ||
Object.keys(outSockets).forEach(function (port) { | ||
var socket = outSockets[port]; | ||
socket.from.process.component.outPorts[socket.from.port].detach(socket); | ||
} | ||
}); | ||
outSockets = {}; | ||
inSockets = {}; | ||
return callback(null, received); | ||
}); | ||
// Start network | ||
return network.start(function(err) { | ||
var i, inputMap, len, port, portDef, process, results, value; | ||
callback(null, received); | ||
network.removeListener('process-error', onError); | ||
}; | ||
network.once('end', onEnd); // Start network | ||
network.start(function (err) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
// Send inputs | ||
results = []; | ||
for (i = 0, len = inputs.length; i < len; i++) { | ||
inputMap = inputs[i]; | ||
results.push((function() { | ||
var results1; | ||
results1 = []; | ||
for (port in inputMap) { | ||
value = inputMap[port]; | ||
if (!inSockets[port]) { | ||
portDef = network.graph.inports[port]; | ||
process = network.getNode(portDef.process); | ||
inSockets[port] = internalSocket.createSocket(); | ||
process.component.inPorts[portDef.port].attach(inSockets[port]); | ||
} | ||
callback(err); | ||
return; | ||
} // Send inputs | ||
for (var i = 0; i < inputs.length; i += 1) { | ||
var inputMap = inputs[i]; | ||
var keys = Object.keys(inputMap); | ||
for (var j = 0; j < keys.length; j += 1) { | ||
var port = keys[j]; | ||
var value = inputMap[port]; | ||
if (!inSockets[port]) { | ||
var portDef = network.graph.inports[port]; | ||
var _process = network.getNode(portDef.process); | ||
inSockets[port] = internalSocket.createSocket(); | ||
_process.component.inPorts[portDef.port].attach(inSockets[port]); | ||
} | ||
try { | ||
if (IP.isIP(value)) { | ||
inSockets[port].post(value); | ||
continue; | ||
} else { | ||
inSockets[port].post(new IP('data', value)); | ||
} | ||
results1.push(inSockets[port].post(new IP('data', value))); | ||
} catch (e) { | ||
callback(e); | ||
network.removeListener('process-error', onError); | ||
network.removeListener('end', onEnd); | ||
return; | ||
} | ||
return results1; | ||
})()); | ||
} | ||
} | ||
return results; | ||
}); | ||
}; | ||
} | ||
getType = function(inputs, network) { | ||
var key, maps, value; | ||
if (typeof inputs !== 'object') { | ||
// Scalar values are always simple inputs | ||
function getType(inputs, network) { | ||
// Scalar values are always simple inputs | ||
if (_typeof(inputs) !== 'object') { | ||
return 'simple'; | ||
} | ||
if (Array.isArray(inputs)) { | ||
maps = inputs.filter(function(entry) { | ||
var maps = inputs.filter(function (entry) { | ||
return getType(entry, network) === 'map'; | ||
}); | ||
}); // If each member if the array is an input map, this is a sequence | ||
if (maps.length === inputs.length) { | ||
// If each member if the array is an input map, this is a sequence | ||
return 'sequence'; | ||
} | ||
// Otherwise arrays must be simple inputs | ||
} // Otherwise arrays must be simple inputs | ||
return 'simple'; | ||
} | ||
if (!Object.keys(inputs).length) { | ||
// Empty objects can't be maps | ||
} // Empty objects can't be maps | ||
var keys = Object.keys(inputs); | ||
if (!keys.length) { | ||
return 'simple'; | ||
} | ||
for (key in inputs) { | ||
value = inputs[key]; | ||
for (var i = 0; i < keys.length; i += 1) { | ||
var key = keys[i]; | ||
if (!network.graph.inports[key]) { | ||
@@ -227,36 +269,38 @@ return 'simple'; | ||
} | ||
return 'map'; | ||
}; | ||
} | ||
prepareInputMap = function(inputs, inputType, network) { | ||
var inPort, map; | ||
function prepareInputMap(inputs, inputType, network) { | ||
// Sequence we can use as-is | ||
if (inputType === 'sequence') { | ||
// Sequence we can use as-is | ||
return inputs; | ||
} | ||
} // We can turn a map to a sequence by wrapping it in an array | ||
if (inputType === 'map') { | ||
// We can turn a map to a sequence by wrapping it in an array | ||
return [inputs]; | ||
} | ||
// Simple inputs need to be converted to a sequence | ||
inPort = Object.keys(network.graph.inports)[0]; | ||
if (network.graph.inports.in) { | ||
// If we have a port named "IN", send to that | ||
} // Simple inputs need to be converted to a sequence | ||
var inPort = Object.keys(network.graph.inports)[0]; // If we have a port named "IN", send to that | ||
if (network.graph.inports["in"]) { | ||
inPort = 'in'; | ||
} | ||
map = {}; | ||
var map = {}; | ||
map[inPort] = inputs; | ||
return [map]; | ||
}; | ||
} | ||
normalizeOutput = function(values, options) { | ||
var current, i, len, packet, previous, result; | ||
function normalizeOutput(values, options) { | ||
if (options.raw) { | ||
return values; | ||
} | ||
result = []; | ||
previous = null; | ||
current = result; | ||
for (i = 0, len = values.length; i < len; i++) { | ||
packet = values[i]; | ||
var result = []; | ||
var previous = null; | ||
var current = result; | ||
values.forEach(function (packet) { | ||
if (packet.type === 'openBracket') { | ||
@@ -267,91 +311,113 @@ previous = current; | ||
} | ||
if (packet.type === 'data') { | ||
current.push(packet.data); | ||
} | ||
if (packet.type === 'closeBracket') { | ||
current = previous; | ||
} | ||
} | ||
}); | ||
if (result.length === 1) { | ||
return result[0]; | ||
} | ||
return result; | ||
}; | ||
} | ||
sendOutputMap = function(outputs, resultType, options, callback) { | ||
var errors, i, key, len, map, mappedOutputs, outputKeys, packets, port, result, val, withValue; | ||
function sendOutputMap(outputs, resultType, options, callback) { | ||
// First check if the output sequence contains errors | ||
errors = outputs.filter(function(map) { | ||
var errors = outputs.filter(function (map) { | ||
return map.error != null; | ||
}).map(function(map) { | ||
}).map(function (map) { | ||
return map.error; | ||
}); | ||
if (errors.length) { | ||
return callback(normalizeOutput(errors, options)); | ||
callback(normalizeOutput(errors, options)); | ||
return; | ||
} | ||
if (resultType === 'sequence') { | ||
return callback(null, outputs.map(function(map) { | ||
var key, res, val; | ||
res = {}; | ||
for (key in map) { | ||
val = map[key]; | ||
callback(null, outputs.map(function (map) { | ||
var res = {}; | ||
Object.keys(map).forEach(function (key) { | ||
var val = map[key]; | ||
if (options.raw) { | ||
res[key] = val; | ||
continue; | ||
return; | ||
} | ||
res[key] = normalizeOutput([val], options); | ||
} | ||
}); | ||
return res; | ||
})); | ||
} | ||
// Flatten the sequence | ||
mappedOutputs = {}; | ||
for (i = 0, len = outputs.length; i < len; i++) { | ||
map = outputs[i]; | ||
for (key in map) { | ||
val = map[key]; | ||
return; | ||
} // Flatten the sequence | ||
var mappedOutputs = {}; | ||
outputs.forEach(function (map) { | ||
Object.keys(map).forEach(function (key) { | ||
var val = map[key]; | ||
if (!mappedOutputs[key]) { | ||
mappedOutputs[key] = []; | ||
} | ||
mappedOutputs[key].push(val); | ||
} | ||
} | ||
outputKeys = Object.keys(mappedOutputs); | ||
withValue = outputKeys.filter(function(outport) { | ||
}); | ||
}); | ||
var outputKeys = Object.keys(mappedOutputs); | ||
var withValue = outputKeys.filter(function (outport) { | ||
return mappedOutputs[outport].length > 0; | ||
}); | ||
if (withValue.length === 0) { | ||
// No output | ||
return callback(null); | ||
callback(null); | ||
return; | ||
} | ||
if (withValue.length === 1 && resultType === 'simple') { | ||
// Single outport | ||
return callback(null, normalizeOutput(mappedOutputs[withValue[0]], options)); | ||
callback(null, normalizeOutput(mappedOutputs[withValue[0]], options)); | ||
return; | ||
} | ||
result = {}; | ||
for (port in mappedOutputs) { | ||
packets = mappedOutputs[port]; | ||
var result = {}; | ||
Object.keys(mappedOutputs).forEach(function (port) { | ||
var packets = mappedOutputs[port]; | ||
result[port] = normalizeOutput(packets, options); | ||
} | ||
return callback(null, result); | ||
}; | ||
}); | ||
callback(null, result); | ||
} | ||
exports.asCallback = function(component, options) { | ||
exports.asCallback = function asCallback(component, options) { | ||
options = normalizeOptions(options, component); | ||
return function(inputs, callback) { | ||
return prepareNetwork(component, options, function(err, network) { | ||
var inputMap, resultType; | ||
return function (inputs, callback) { | ||
prepareNetwork(component, options, function (err, network) { | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
resultType = getType(inputs, network); | ||
inputMap = prepareInputMap(inputs, resultType, network); | ||
return runNetwork(network, inputMap, options, function(err, outputMap) { | ||
if (err) { | ||
return callback(err); | ||
if (options.networkCallback) { | ||
options.networkCallback(network); | ||
} | ||
var resultType = getType(inputs, network); | ||
var inputMap = prepareInputMap(inputs, resultType, network); | ||
runNetwork(network, inputMap, options, function (err2, outputMap) { | ||
if (err2) { | ||
callback(err2); | ||
return; | ||
} | ||
return sendOutputMap(outputMap, resultType, options, callback); | ||
sendOutputMap(outputMap, resultType, options, callback); | ||
}); | ||
}); | ||
}; | ||
}; | ||
}; |
@@ -0,15 +1,28 @@ | ||
"use strict"; | ||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } | ||
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } | ||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } | ||
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } | ||
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } | ||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } | ||
// NoFlo - Flow-Based Programming for JavaScript | ||
// (c) 2018 Flowhub UG | ||
// NoFlo may be freely distributed under the MIT license | ||
var Component, getParams; | ||
var getParams = require('get-function-params'); | ||
getParams = require('get-function-params'); | ||
({Component} = require('./Component')); | ||
// ## asComponent generator API | ||
var _require = require('./Component'), | ||
Component = _require.Component; // ## asComponent generator API | ||
// | ||
// asComponent is a helper for turning JavaScript functions into | ||
// NoFlo components. | ||
// | ||
// Each call to this function returns a component instance where | ||
@@ -19,11 +32,14 @@ // the input parameters of the given function are converted into | ||
// results of the function execution. | ||
// | ||
// Variants supported: | ||
// * Regular synchronous functions: return value gets sent to `out`. Thrown errors get sent to `error` | ||
// * Functions returning a Promise: resolved promises get sent to `out`, rejected promises to `error` | ||
// * Functions taking a Node.js style asynchronous callback: `err` argument to callback gets sent to `error`, result gets sent to `out` | ||
// | ||
// * Regular synchronous functions: return value gets sent to `out`. | ||
// Thrown errors get sent to `error` | ||
// * Functions returning a Promise: resolved promises get sent to `out`, | ||
// rejected promises to `error` | ||
// * Functions taking a Node.js style asynchronous callback: `err` argument | ||
// to callback gets sent to `error`, result gets sent to `out` | ||
// | ||
// Usage example: | ||
// | ||
// exports.getComponent = function () { | ||
@@ -34,11 +50,11 @@ // return noflo.asComponent(Math.random, { | ||
// }; | ||
// | ||
// ### Wrapping built-in functions | ||
// Built-in JavaScript functions don't make their arguments introspectable. Because of this, these | ||
// cannot be directly converted to components. You'll have to provide a wrapper JavaScript function to make | ||
// the arguments appear as ports. | ||
// | ||
// Built-in JavaScript functions don't make their arguments introspectable. | ||
// Because of this, these cannot be directly converted to components. | ||
// You'll have to provide a wrapper JavaScript function to make the arguments appear as ports. | ||
// | ||
// Example: | ||
// | ||
// exports.getComponent = function () { | ||
@@ -52,33 +68,38 @@ // return noflo.asComponent(function (selector) { | ||
// }; | ||
// | ||
// ### Default values | ||
// | ||
// Function arguments with a default value are supported in ES6 environments. | ||
// The default arguments are visible via the component's port interface. | ||
// | ||
// However, ES5 transpilation doesn't work with default values. | ||
// In these cases the port with a default won't be visible. It is | ||
// recommended to use default values only with components that don't need to run in legacy browsers. | ||
// Function arguments with a default value are supported in ES6 environments. The default arguments are visible via the component's | ||
// port interface. | ||
// However, ES5 transpilation doesn't work with default values. In these cases the port with a default won't be visible. It is | ||
// recommended to use default values only with components that don't need to run in legacy browsers. | ||
exports.asComponent = function(func, options) { | ||
var c, hasCallback, i, len, p, params, portOptions; | ||
hasCallback = false; | ||
params = getParams(func).filter(function(p) { | ||
exports.asComponent = function asComponent(func, options) { | ||
var hasCallback = false; | ||
var params = getParams(func).filter(function (p) { | ||
if (p.param !== 'callback') { | ||
return true; | ||
} | ||
hasCallback = true; | ||
return false; | ||
}); | ||
c = new Component(options); | ||
for (i = 0, len = params.length; i < len; i++) { | ||
p = params[i]; | ||
portOptions = { | ||
var c = new Component(options); | ||
params.forEach(function (p) { | ||
var portOptions = { | ||
required: true | ||
}; | ||
if (typeof p.default !== 'undefined') { | ||
portOptions.default = p.default; | ||
if (typeof p["default"] !== 'undefined') { | ||
portOptions["default"] = p["default"]; | ||
portOptions.required = false; | ||
} | ||
c.inPorts.add(p.param, portOptions); | ||
c.forwardBrackets[p.param] = ['out', 'error']; | ||
} | ||
}); | ||
if (!params.length) { | ||
@@ -89,9 +110,12 @@ c.inPorts.add('in', { | ||
} | ||
c.outPorts.add('out'); | ||
c.outPorts.add('error'); | ||
c.process(function(input, output) { | ||
var j, len1, res, values; | ||
c.process(function (input, output) { | ||
var values; | ||
if (params.length) { | ||
for (j = 0, len1 = params.length; j < len1; j++) { | ||
p = params[j]; | ||
for (var i = 0; i < params.length; i += 1) { | ||
var p = params[i]; | ||
if (!input.hasData(p.param)) { | ||
@@ -101,3 +125,4 @@ return; | ||
} | ||
values = params.map(function(p) { | ||
values = params.map(function (p) { | ||
return input.getData(p.param); | ||
@@ -109,22 +134,28 @@ }); | ||
} | ||
input.getData('in'); | ||
values = []; | ||
} | ||
if (hasCallback) { | ||
// Handle Node.js style async functions | ||
values.push(function(err, res) { | ||
values.push(function (err, res) { | ||
if (err) { | ||
return output.done(err); | ||
output.done(err); | ||
return; | ||
} | ||
return output.sendDone(res); | ||
output.sendDone(res); | ||
}); | ||
res = func.apply(null, values); | ||
func.apply(void 0, _toConsumableArray(values)); | ||
return; | ||
} | ||
res = func.apply(null, values); | ||
if (res && typeof res === 'object' && typeof res.then === 'function') { | ||
var res = func.apply(void 0, _toConsumableArray(values)); | ||
if (res && _typeof(res) === 'object' && typeof res.then === 'function') { | ||
// Result is a Promise, resolve and handle | ||
res.then(function(val) { | ||
res.then(function (val) { | ||
return output.sendDone(val); | ||
}, function(err) { | ||
}, function (err) { | ||
return output.done(err); | ||
@@ -134,5 +165,6 @@ }); | ||
} | ||
return output.sendDone(res); | ||
output.sendDone(res); | ||
}); | ||
return c; | ||
}; | ||
}; |
@@ -0,186 +1,260 @@ | ||
"use strict"; | ||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } | ||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } | ||
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } | ||
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } | ||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } | ||
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } | ||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } | ||
// NoFlo - Flow-Based Programming for JavaScript | ||
// (c) 2014-2017 Flowhub UG | ||
// NoFlo may be freely distributed under the MIT license | ||
var BasePort, EventEmitter, validTypes; | ||
var _require = require('events'), | ||
EventEmitter = _require.EventEmitter; // ## NoFlo Port Base class | ||
// | ||
// Base port type used for options normalization. Both inports and outports extend this class. | ||
// The list of valid datatypes for ports. | ||
({EventEmitter} = require('events')); | ||
// ## NoFlo Port Base class | ||
var validTypes = ['all', 'string', 'number', 'int', 'object', 'array', 'boolean', 'color', 'date', 'bang', 'function', 'buffer', 'stream']; | ||
// Base port type used for options normalization. Both inports and outports extend this class. | ||
function handleOptions() { | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
// We default to the `all` type if no explicit datatype | ||
// was provided | ||
var datatype = options.datatype || 'all'; // Normalize the legacy `integer` type to `int`. | ||
// The list of valid datatypes for ports. | ||
validTypes = ['all', 'string', 'number', 'int', 'object', 'array', 'boolean', 'color', 'date', 'bang', 'function', 'buffer', 'stream']; | ||
if (datatype === 'integer') { | ||
datatype = 'int'; | ||
} // By default ports are not required for graph execution | ||
BasePort = class BasePort extends EventEmitter { | ||
constructor(options) { | ||
super(); | ||
// Options holds all options of the current port | ||
this.options = this.handleOptions(options); | ||
// Sockets list contains all currently attached | ||
var required = options.required || false; // Ensure datatype defined for the port is valid | ||
if (validTypes.indexOf(datatype) === -1) { | ||
throw new Error("Invalid port datatype '".concat(datatype, "' specified, valid are ").concat(validTypes.join(', '))); | ||
} // Ensure schema defined for the port is valid | ||
var schema = options.schema || options.type; | ||
if (schema && schema.indexOf('/') === -1) { | ||
throw new Error("Invalid port schema '".concat(schema, "' specified. Should be URL or MIME type")); | ||
} | ||
/* eslint-disable prefer-object-spread */ | ||
return Object.assign({}, options, { | ||
datatype: datatype, | ||
required: required, | ||
schema: schema | ||
}); | ||
} | ||
module.exports = /*#__PURE__*/function (_EventEmitter) { | ||
_inherits(BasePort, _EventEmitter); | ||
var _super = _createSuper(BasePort); | ||
function BasePort(options) { | ||
var _this; | ||
_classCallCheck(this, BasePort); | ||
_this = _super.call(this); // Options holds all options of the current port | ||
_this.options = handleOptions(options); // Sockets list contains all currently attached | ||
// connections to the port | ||
this.sockets = []; | ||
// Name of the graph node this port is in | ||
this.node = null; | ||
// Name of the port | ||
this.name = null; | ||
_this.sockets = []; // Name of the graph node this port is in | ||
_this.node = null; // Name of the port | ||
_this.name = null; | ||
return _this; | ||
} | ||
handleOptions(options) { | ||
if (!options) { | ||
options = {}; | ||
_createClass(BasePort, [{ | ||
key: "getId", | ||
value: function getId() { | ||
if (!this.node || !this.name) { | ||
return 'Port'; | ||
} | ||
return "".concat(this.node, " ").concat(this.name.toUpperCase()); | ||
} | ||
if (!options.datatype) { | ||
// We default to the `all` type if no explicit datatype | ||
// was provided | ||
options.datatype = 'all'; | ||
}, { | ||
key: "getDataType", | ||
value: function getDataType() { | ||
return this.options.datatype; | ||
} | ||
if (options.required === void 0) { | ||
// By default ports are not required for graph execution | ||
options.required = false; | ||
}, { | ||
key: "getSchema", | ||
value: function getSchema() { | ||
return this.options.schema || null; | ||
} | ||
if (options.datatype === 'integer') { | ||
// Normalize the legacy `integer` type to `int`. | ||
options.datatype = 'int'; | ||
}, { | ||
key: "getDescription", | ||
value: function getDescription() { | ||
return this.options.description; | ||
} | ||
// Ensure datatype defined for the port is valid | ||
if (validTypes.indexOf(options.datatype) === -1) { | ||
throw new Error(`Invalid port datatype '${options.datatype}' specified, valid are ${validTypes.join(', ')}`); | ||
} | ||
// Ensure schema defined for the port is valid | ||
if (options.type && !options.schema) { | ||
options.schema = options.type; | ||
delete options.type; | ||
} | ||
if (options.schema && options.schema.indexOf('/') === -1) { | ||
throw new Error(`Invalid port schema '${options.schema}' specified. Should be URL or MIME type`); | ||
} | ||
return options; | ||
} | ||
}, { | ||
key: "attach", | ||
value: function attach(socket) { | ||
var index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; | ||
var idx = index; | ||
getId() { | ||
if (!(this.node && this.name)) { | ||
return 'Port'; | ||
if (!this.isAddressable() || index === null) { | ||
idx = this.sockets.length; | ||
} | ||
this.sockets[idx] = socket; | ||
this.attachSocket(socket, idx); | ||
if (this.isAddressable()) { | ||
this.emit('attach', socket, idx); | ||
return; | ||
} | ||
this.emit('attach', socket); | ||
} | ||
return `${this.node} ${this.name.toUpperCase()}`; | ||
} | ||
/* eslint-disable class-methods-use-this */ | ||
getDataType() { | ||
return this.options.datatype; | ||
} | ||
}, { | ||
key: "attachSocket", | ||
value: function attachSocket() {} | ||
}, { | ||
key: "detach", | ||
value: function detach(socket) { | ||
var index = this.sockets.indexOf(socket); | ||
getSchema() { | ||
return this.options.schema || null; | ||
} | ||
if (index === -1) { | ||
return; | ||
} | ||
getDescription() { | ||
return this.options.description; | ||
} | ||
this.sockets[index] = undefined; | ||
attach(socket, index = null) { | ||
if (!this.isAddressable() || index === null) { | ||
index = this.sockets.length; | ||
} | ||
this.sockets[index] = socket; | ||
this.attachSocket(socket, index); | ||
if (this.isAddressable()) { | ||
this.emit('attach', socket, index); | ||
return; | ||
} | ||
return this.emit('attach', socket); | ||
} | ||
if (this.isAddressable()) { | ||
this.emit('detach', socket, index); | ||
return; | ||
} | ||
attachSocket() {} | ||
detach(socket) { | ||
var index; | ||
index = this.sockets.indexOf(socket); | ||
if (index === -1) { | ||
return; | ||
this.emit('detach', socket); | ||
} | ||
this.sockets[index] = void 0; | ||
if (this.isAddressable()) { | ||
this.emit('detach', socket, index); | ||
return; | ||
} | ||
return this.emit('detach', socket); | ||
} | ||
}, { | ||
key: "isAddressable", | ||
value: function isAddressable() { | ||
if (this.options.addressable) { | ||
return true; | ||
} | ||
isAddressable() { | ||
if (this.options.addressable) { | ||
return true; | ||
return false; | ||
} | ||
return false; | ||
} | ||
}, { | ||
key: "isBuffered", | ||
value: function isBuffered() { | ||
if (this.options.buffered) { | ||
return true; | ||
} | ||
isBuffered() { | ||
if (this.options.buffered) { | ||
return true; | ||
return false; | ||
} | ||
return false; | ||
} | ||
}, { | ||
key: "isRequired", | ||
value: function isRequired() { | ||
if (this.options.required) { | ||
return true; | ||
} | ||
isRequired() { | ||
if (this.options.required) { | ||
return true; | ||
return false; | ||
} | ||
return false; | ||
} | ||
}, { | ||
key: "isAttached", | ||
value: function isAttached() { | ||
var socketId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; | ||
isAttached(socketId = null) { | ||
if (this.isAddressable() && socketId !== null) { | ||
if (this.sockets[socketId]) { | ||
if (this.isAddressable() && socketId !== null) { | ||
if (this.sockets[socketId]) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
if (this.sockets.length) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
if (this.sockets.length) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
}, { | ||
key: "listAttached", | ||
value: function listAttached() { | ||
var attached = []; | ||
listAttached() { | ||
var attached, i, idx, len, ref, socket; | ||
attached = []; | ||
ref = this.sockets; | ||
for (idx = i = 0, len = ref.length; i < len; idx = ++i) { | ||
socket = ref[idx]; | ||
if (!socket) { | ||
continue; | ||
for (var idx = 0; idx < this.sockets.length; idx += 1) { | ||
var socket = this.sockets[idx]; | ||
if (socket) { | ||
attached.push(idx); | ||
} | ||
} | ||
attached.push(idx); | ||
return attached; | ||
} | ||
return attached; | ||
} | ||
}, { | ||
key: "isConnected", | ||
value: function isConnected() { | ||
var socketId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; | ||
isConnected(socketId = null) { | ||
var connected; | ||
if (this.isAddressable()) { | ||
if (socketId === null) { | ||
throw new Error(`${this.getId()}: Socket ID required`); | ||
if (this.isAddressable()) { | ||
if (socketId === null) { | ||
throw new Error("".concat(this.getId(), ": Socket ID required")); | ||
} | ||
if (!this.sockets[socketId]) { | ||
throw new Error("".concat(this.getId(), ": Socket ").concat(socketId, " not available")); | ||
} | ||
return this.sockets[socketId].isConnected(); | ||
} | ||
if (!this.sockets[socketId]) { | ||
throw new Error(`${this.getId()}: Socket ${socketId} not available`); | ||
} | ||
return this.sockets[socketId].isConnected(); | ||
var connected = false; | ||
this.sockets.forEach(function (socket) { | ||
if (!socket) { | ||
return; | ||
} | ||
if (socket.isConnected()) { | ||
connected = true; | ||
} | ||
}); | ||
return connected; | ||
} | ||
connected = false; | ||
this.sockets.forEach(function(socket) { | ||
if (!socket) { | ||
return; | ||
} | ||
if (socket.isConnected()) { | ||
return connected = true; | ||
} | ||
}); | ||
return connected; | ||
} | ||
/* eslint-disable class-methods-use-this */ | ||
canAttach() { | ||
return true; | ||
} | ||
}, { | ||
key: "canAttach", | ||
value: function canAttach() { | ||
return true; | ||
} | ||
}]); | ||
}; | ||
module.exports = BasePort; | ||
return BasePort; | ||
}(EventEmitter); |
1662
lib/Component.js
@@ -1,314 +0,399 @@ | ||
// NoFlo - Flow-Based Programming for JavaScript | ||
// (c) 2013-2017 Flowhub UG | ||
// (c) 2011-2012 Henri Bergius, Nemein | ||
// NoFlo may be freely distributed under the MIT license | ||
var Component, EventEmitter, IP, ProcessContext, ProcessInput, ProcessOutput, debug, debugBrackets, debugSend, ports, | ||
boundMethodCheck = function(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new Error('Bound instance method accessed before binding'); } }; | ||
"use strict"; | ||
({EventEmitter} = require('events')); | ||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
ports = require('./Ports'); | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
IP = require('./IP'); | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
debug = require('debug')('noflo:component'); | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } | ||
debugBrackets = require('debug')('noflo:component:brackets'); | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } | ||
debugSend = require('debug')('noflo:component:send'); | ||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } | ||
Component = (function() { | ||
// ## NoFlo Component Base class | ||
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } | ||
// The `noflo.Component` interface provides a way to instantiate | ||
// and extend NoFlo components. | ||
class Component extends EventEmitter { | ||
constructor(options) { | ||
var ref, ref1, ref2; | ||
super(); | ||
// ### Error emitting helper | ||
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } | ||
// If component has an `error` outport that is connected, errors | ||
// are sent as IP objects there. If the port is not connected, | ||
// errors are thrown. | ||
this.error = this.error.bind(this); | ||
if (!options) { | ||
options = {}; | ||
} | ||
if (!options.inPorts) { | ||
// Prepare inports, if any were given in options. | ||
// They can also be set up imperatively after component | ||
// instantiation by using the `component.inPorts.add` | ||
// method. | ||
options.inPorts = {}; | ||
} | ||
if (options.inPorts instanceof ports.InPorts) { | ||
this.inPorts = options.inPorts; | ||
} else { | ||
this.inPorts = new ports.InPorts(options.inPorts); | ||
} | ||
if (!options.outPorts) { | ||
// Prepare outports, if any were given in options. | ||
// They can also be set up imperatively after component | ||
// instantiation by using the `component.outPorts.add` | ||
// method. | ||
options.outPorts = {}; | ||
} | ||
if (options.outPorts instanceof ports.OutPorts) { | ||
this.outPorts = options.outPorts; | ||
} else { | ||
this.outPorts = new ports.OutPorts(options.outPorts); | ||
} | ||
if (options.icon) { | ||
// Set the default component icon and description | ||
this.icon = options.icon; | ||
} | ||
if (options.description) { | ||
this.description = options.description; | ||
} | ||
// Initially the component is not started | ||
this.started = false; | ||
this.load = 0; | ||
// Whether the component should keep send packets | ||
// out in the order they were received | ||
this.ordered = (ref = options.ordered) != null ? ref : false; | ||
this.autoOrdering = (ref1 = options.autoOrdering) != null ? ref1 : null; | ||
// Queue for handling ordered output packets | ||
this.outputQ = []; | ||
// Context used for bracket forwarding | ||
this.bracketContext = { | ||
in: {}, | ||
out: {} | ||
}; | ||
// Whether the component should activate when it | ||
// receives packets | ||
this.activateOnInput = (ref2 = options.activateOnInput) != null ? ref2 : true; | ||
// Bracket forwarding rules. By default we forward | ||
// brackets from `in` port to `out` and `error` ports. | ||
this.forwardBrackets = { | ||
in: ['out', 'error'] | ||
}; | ||
if ('forwardBrackets' in options) { | ||
this.forwardBrackets = options.forwardBrackets; | ||
} | ||
// The component's process function can either be | ||
// passed in options, or given imperatively after | ||
// instantation using the `component.process` method. | ||
if (typeof options.process === 'function') { | ||
this.process(options.process); | ||
} | ||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } | ||
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } | ||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } | ||
// NoFlo - Flow-Based Programming for JavaScript | ||
// (c) 2013-2017 Flowhub UG | ||
// (c) 2011-2012 Henri Bergius, Nemein | ||
// NoFlo may be freely distributed under the MIT license | ||
/* eslint-disable | ||
class-methods-use-this, | ||
no-underscore-dangle, | ||
*/ | ||
var _require = require('events'), | ||
EventEmitter = _require.EventEmitter; | ||
var debug = require('debug')('noflo:component'); | ||
var debugBrackets = require('debug')('noflo:component:brackets'); | ||
var debugSend = require('debug')('noflo:component:send'); | ||
var ports = require('./Ports'); | ||
var ProcessContext = require('./ProcessContext'); | ||
var ProcessInput = require('./ProcessInput'); | ||
var ProcessOutput = require('./ProcessOutput'); // ## NoFlo Component Base class | ||
// | ||
// The `noflo.Component` interface provides a way to instantiate | ||
// and extend NoFlo components. | ||
var Component = /*#__PURE__*/function (_EventEmitter) { | ||
_inherits(Component, _EventEmitter); | ||
var _super = _createSuper(Component); | ||
function Component() { | ||
var _this; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
_classCallCheck(this, Component); | ||
_this = _super.call(this); | ||
var opts = options; // Prepare inports, if any were given in options. | ||
// They can also be set up imperatively after component | ||
// instantiation by using the `component.inPorts.add` | ||
// method. | ||
if (!opts.inPorts) { | ||
opts.inPorts = {}; | ||
} | ||
getDescription() { | ||
if (opts.inPorts instanceof ports.InPorts) { | ||
_this.inPorts = opts.inPorts; | ||
} else { | ||
_this.inPorts = new ports.InPorts(opts.inPorts); | ||
} // Prepare outports, if any were given in opts. | ||
// They can also be set up imperatively after component | ||
// instantiation by using the `component.outPorts.add` | ||
// method. | ||
if (!opts.outPorts) { | ||
opts.outPorts = {}; | ||
} | ||
if (opts.outPorts instanceof ports.OutPorts) { | ||
_this.outPorts = opts.outPorts; | ||
} else { | ||
_this.outPorts = new ports.OutPorts(opts.outPorts); | ||
} // Set the default component icon and description | ||
_this.icon = opts.icon ? opts.icon : _this.constructor.icon; | ||
_this.description = opts.description ? opts.description : _this.constructor.description; // Initially the component is not started | ||
_this.started = false; | ||
_this.load = 0; // Whether the component should keep send packets | ||
// out in the order they were received | ||
_this.ordered = opts.ordered != null ? opts.ordered : false; | ||
_this.autoOrdering = opts.autoOrdering != null ? opts.autoOrdering : null; // Queue for handling ordered output packets | ||
_this.outputQ = []; // Context used for bracket forwarding | ||
_this.bracketContext = { | ||
"in": {}, | ||
out: {} | ||
}; // Whether the component should activate when it | ||
// receives packets | ||
_this.activateOnInput = opts.activateOnInput != null ? opts.activateOnInput : true; // Bracket forwarding rules. By default we forward | ||
// brackets from `in` port to `out` and `error` ports. | ||
_this.forwardBrackets = { | ||
"in": ['out', 'error'] | ||
}; | ||
if ('forwardBrackets' in opts) { | ||
_this.forwardBrackets = opts.forwardBrackets; | ||
} // The component's process function can either be | ||
// passed in opts, or given imperatively after | ||
// instantation using the `component.process` method. | ||
if (typeof opts.process === 'function') { | ||
_this.process(opts.process); | ||
} | ||
return _this; | ||
} | ||
_createClass(Component, [{ | ||
key: "getDescription", | ||
value: function getDescription() { | ||
return this.description; | ||
} | ||
isReady() { | ||
}, { | ||
key: "isReady", | ||
value: function isReady() { | ||
return true; | ||
} | ||
isSubgraph() { | ||
}, { | ||
key: "isSubgraph", | ||
value: function isSubgraph() { | ||
return false; | ||
} | ||
setIcon(icon) { | ||
}, { | ||
key: "setIcon", | ||
value: function setIcon(icon) { | ||
this.icon = icon; | ||
return this.emit('icon', this.icon); | ||
this.emit('icon', this.icon); | ||
} | ||
getIcon() { | ||
}, { | ||
key: "getIcon", | ||
value: function getIcon() { | ||
return this.icon; | ||
} | ||
} // ### Error emitting helper | ||
// | ||
// If component has an `error` outport that is connected, errors | ||
// are sent as IP objects there. If the port is not connected, | ||
// errors are thrown. | ||
error(e, groups = [], errorPort = 'error', scope = null) { | ||
var group, i, j, len1, len2; | ||
boundMethodCheck(this, Component); | ||
}, { | ||
key: "error", | ||
value: function error(e) { | ||
var _this2 = this; | ||
var groups = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; | ||
var errorPort = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'error'; | ||
var scope = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; | ||
if (this.outPorts[errorPort] && (this.outPorts[errorPort].isAttached() || !this.outPorts[errorPort].isRequired())) { | ||
for (i = 0, len1 = groups.length; i < len1; i++) { | ||
group = groups[i]; | ||
this.outPorts[errorPort].openBracket(group, { | ||
groups.forEach(function (group) { | ||
_this2.outPorts[errorPort].openBracket(group, { | ||
scope: scope | ||
}); | ||
} | ||
}); | ||
this.outPorts[errorPort].data(e, { | ||
scope: scope | ||
}); | ||
for (j = 0, len2 = groups.length; j < len2; j++) { | ||
group = groups[j]; | ||
this.outPorts[errorPort].closeBracket(group, { | ||
groups.forEach(function (group) { | ||
_this2.outPorts[errorPort].closeBracket(group, { | ||
scope: scope | ||
}); | ||
} | ||
}); | ||
return; | ||
} | ||
throw e; | ||
} | ||
// ### Setup | ||
} // ### Setup | ||
// | ||
// The setUp method is for component-specific initialization. | ||
// Called at network start-up. | ||
// | ||
// Override in component implementation to do component-specific | ||
// setup work. | ||
setUp(callback) { | ||
}, { | ||
key: "setUp", | ||
value: function setUp(callback) { | ||
callback(); | ||
} | ||
// ### Setup | ||
} // ### Setup | ||
// | ||
// The tearDown method is for component-specific cleanup. Called | ||
// at network shutdown | ||
// | ||
// Override in component implementation to do component-specific | ||
// cleanup work, like clearing any accumulated state. | ||
tearDown(callback) { | ||
}, { | ||
key: "tearDown", | ||
value: function tearDown(callback) { | ||
callback(); | ||
} | ||
} // ### Start | ||
// | ||
// Called when network starts. This sets calls the setUp | ||
// method and sets the component to a started state. | ||
// ### Start | ||
}, { | ||
key: "start", | ||
value: function start(callback) { | ||
var _this3 = this; | ||
// Called when network starts. This sets calls the setUp | ||
// method and sets the component to a started state. | ||
start(callback) { | ||
if (this.isStarted()) { | ||
return callback(); | ||
callback(); | ||
return; | ||
} | ||
this.setUp((err) => { | ||
this.setUp(function (err) { | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
this.started = true; | ||
this.emit('start'); | ||
_this3.started = true; | ||
_this3.emit('start'); | ||
callback(null); | ||
}); | ||
} | ||
// ### Shutdown | ||
} // ### Shutdown | ||
// | ||
// Called when network is shut down. This sets calls the | ||
// tearDown method and sets the component back to a | ||
// non-started state. | ||
// | ||
// The callback is called when tearDown finishes and | ||
// all active processing contexts have ended. | ||
shutdown(callback) { | ||
var finalize; | ||
finalize = () => { | ||
var inPort, inPorts, portName; | ||
}, { | ||
key: "shutdown", | ||
value: function shutdown(callback) { | ||
var _this4 = this; | ||
var finalize = function finalize() { | ||
// Clear contents of inport buffers | ||
inPorts = this.inPorts.ports || this.inPorts; | ||
for (portName in inPorts) { | ||
inPort = inPorts[portName]; | ||
var inPorts = _this4.inPorts.ports || _this4.inPorts; | ||
Object.keys(inPorts).forEach(function (portName) { | ||
var inPort = inPorts[portName]; | ||
if (typeof inPort.clear !== 'function') { | ||
continue; | ||
return; | ||
} | ||
inPort.clear(); | ||
} | ||
// Clear bracket context | ||
this.bracketContext = { | ||
in: {}, | ||
}); // Clear bracket context | ||
_this4.bracketContext = { | ||
"in": {}, | ||
out: {} | ||
}; | ||
if (!this.isStarted()) { | ||
return callback(); | ||
if (!_this4.isStarted()) { | ||
callback(); | ||
return; | ||
} | ||
this.started = false; | ||
this.emit('end'); | ||
_this4.started = false; | ||
_this4.emit('end'); | ||
callback(); | ||
}; | ||
// Tell the component that it is time to shut down | ||
this.tearDown((err) => { | ||
var checkLoad; | ||
}; // Tell the component that it is time to shut down | ||
this.tearDown(function (err) { | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
if (this.load > 0) { | ||
if (_this4.load > 0) { | ||
// Some in-flight processes, wait for them to finish | ||
checkLoad = function(load) { | ||
var checkLoad = function checkLoad(load) { | ||
if (load > 0) { | ||
return; | ||
} | ||
this.removeListener('deactivate', checkLoad); | ||
_this4.removeListener('deactivate', checkLoad); | ||
finalize(); | ||
}; | ||
this.on('deactivate', checkLoad); | ||
_this4.on('deactivate', checkLoad); | ||
return; | ||
} | ||
finalize(); | ||
}); | ||
} | ||
isStarted() { | ||
}, { | ||
key: "isStarted", | ||
value: function isStarted() { | ||
return this.started; | ||
} | ||
} // Ensures braket forwarding map is correct for the existing ports | ||
// Ensures braket forwarding map is correct for the existing ports | ||
prepareForwarding() { | ||
var i, inPort, len1, outPort, outPorts, ref, results, tmp; | ||
ref = this.forwardBrackets; | ||
results = []; | ||
for (inPort in ref) { | ||
outPorts = ref[inPort]; | ||
if (!(inPort in this.inPorts.ports)) { | ||
delete this.forwardBrackets[inPort]; | ||
continue; | ||
}, { | ||
key: "prepareForwarding", | ||
value: function prepareForwarding() { | ||
var _this5 = this; | ||
Object.keys(this.forwardBrackets).forEach(function (inPort) { | ||
var outPorts = _this5.forwardBrackets[inPort]; | ||
if (!(inPort in _this5.inPorts.ports)) { | ||
delete _this5.forwardBrackets[inPort]; | ||
return; | ||
} | ||
tmp = []; | ||
for (i = 0, len1 = outPorts.length; i < len1; i++) { | ||
outPort = outPorts[i]; | ||
if (outPort in this.outPorts.ports) { | ||
var tmp = []; | ||
outPorts.forEach(function (outPort) { | ||
if (outPort in _this5.outPorts.ports) { | ||
tmp.push(outPort); | ||
} | ||
} | ||
}); | ||
if (tmp.length === 0) { | ||
results.push(delete this.forwardBrackets[inPort]); | ||
delete _this5.forwardBrackets[inPort]; | ||
} else { | ||
results.push(this.forwardBrackets[inPort] = tmp); | ||
_this5.forwardBrackets[inPort] = tmp; | ||
} | ||
} | ||
return results; | ||
} | ||
}); | ||
} // Method for determining if a component is using the modern | ||
// NoFlo Process API | ||
// Method for determining if a component is using the modern | ||
// NoFlo Process API | ||
isLegacy() { | ||
}, { | ||
key: "isLegacy", | ||
value: function isLegacy() { | ||
// Process API | ||
if (this.handle) { | ||
// Process API | ||
return false; | ||
} | ||
// Legacy | ||
} // Legacy | ||
return true; | ||
} | ||
} // Sets process handler function | ||
// Sets process handler function | ||
process(handle) { | ||
var name, port, ref; | ||
}, { | ||
key: "process", | ||
value: function process(handle) { | ||
var _this6 = this; | ||
if (typeof handle !== 'function') { | ||
throw new Error("Process handler must be a function"); | ||
throw new Error('Process handler must be a function'); | ||
} | ||
if (!this.inPorts) { | ||
throw new Error("Component ports must be defined before process function"); | ||
throw new Error('Component ports must be defined before process function'); | ||
} | ||
this.prepareForwarding(); | ||
this.handle = handle; | ||
ref = this.inPorts.ports; | ||
for (name in ref) { | ||
port = ref[name]; | ||
((name, port) => { | ||
if (!port.name) { | ||
port.name = name; | ||
} | ||
return port.on('ip', (ip) => { | ||
return this.handleIP(ip, port); | ||
}); | ||
})(name, port); | ||
} | ||
Object.keys(this.inPorts.ports).forEach(function (name) { | ||
var port = _this6.inPorts.ports[name]; | ||
if (!port.name) { | ||
port.name = name; | ||
} | ||
port.on('ip', function (ip) { | ||
return _this6.handleIP(ip, port); | ||
}); | ||
}); | ||
return this; | ||
} | ||
} // Method for checking if a given inport is set up for | ||
// automatic bracket forwarding | ||
// Method for checking if a given inport is set up for | ||
// automatic bracket forwarding | ||
isForwardingInport(port) { | ||
}, { | ||
key: "isForwardingInport", | ||
value: function isForwardingInport(port) { | ||
var portName; | ||
if (typeof port === 'string') { | ||
@@ -319,12 +404,17 @@ portName = port; | ||
} | ||
if (portName in this.forwardBrackets) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
} // Method for checking if a given outport is set up for | ||
// automatic bracket forwarding | ||
// Method for checking if a given outport is set up for | ||
// automatic bracket forwarding | ||
isForwardingOutport(inport, outport) { | ||
var inportName, outportName; | ||
}, { | ||
key: "isForwardingOutport", | ||
value: function isForwardingOutport(inport, outport) { | ||
var inportName; | ||
var outportName; | ||
if (typeof inport === 'string') { | ||
@@ -335,2 +425,3 @@ inportName = inport; | ||
} | ||
if (typeof outport === 'string') { | ||
@@ -341,43 +432,54 @@ outportName = outport; | ||
} | ||
if (!this.forwardBrackets[inportName]) { | ||
return false; | ||
} | ||
if (this.forwardBrackets[inportName].indexOf(outportName) !== -1) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
} // Method for checking whether the component sends packets | ||
// in the same order they were received. | ||
// Method for checking whether the component sends packets | ||
// in the same order they were received. | ||
isOrdered() { | ||
}, { | ||
key: "isOrdered", | ||
value: function isOrdered() { | ||
if (this.ordered) { | ||
return true; | ||
} | ||
if (this.autoOrdering) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
// ### Handling IP objects | ||
} // ### Handling IP objects | ||
// | ||
// The component has received an Information Packet. Call the | ||
// processing function so that firing pattern preconditions can | ||
// be checked and component can do processing as needed. | ||
handleIP(ip, port) { | ||
var buf, context, dataPackets, e, input, output, result; | ||
}, { | ||
key: "handleIP", | ||
value: function handleIP(ip, port) { | ||
var context; | ||
if (!port.options.triggering) { | ||
// If port is non-triggering, we can skip the process function call | ||
return; | ||
} | ||
// If port is non-triggering, we can skip the process function call | ||
if (ip.type === 'openBracket' && this.autoOrdering === null && !this.ordered) { | ||
// Switch component to ordered mode when receiving a stream unless | ||
// auto-ordering is disabled | ||
debug(`${this.nodeId} port '${port.name}' entered auto-ordering mode`); | ||
debug("".concat(this.nodeId, " port '").concat(port.name, "' entered auto-ordering mode")); | ||
this.autoOrdering = true; | ||
} | ||
// Initialize the result object for situations where output needs | ||
} // Initialize the result object for situations where output needs | ||
// to be queued to be kept in order | ||
result = {}; | ||
var result = {}; | ||
if (this.isForwardingInport(port)) { | ||
@@ -387,5 +489,6 @@ // For bracket-forwarding inports we need to initialize a bracket context | ||
if (ip.type === 'openBracket') { | ||
// For forwarding ports openBrackets don't fire | ||
return; | ||
} | ||
// For forwarding ports openBrackets don't fire | ||
if (ip.type === 'closeBracket') { | ||
@@ -398,15 +501,17 @@ // For forwarding ports closeBrackets don't fire | ||
// new closeBracket arrives | ||
buf = port.getBuffer(ip.scope, ip.index); | ||
dataPackets = buf.filter(function(ip) { | ||
return ip.type === 'data'; | ||
var buf = port.getBuffer(ip.scope, ip.index); | ||
var dataPackets = buf.filter(function (p) { | ||
return p.type === 'data'; | ||
}); | ||
if (this.outputQ.length >= this.load && dataPackets.length === 0) { | ||
if (buf[0] !== ip) { | ||
return; | ||
} | ||
// Remove from buffer | ||
} // Remove from buffer | ||
port.get(ip.scope, ip.index); | ||
context = this.getBracketContext('in', port.name, ip.scope, ip.index).pop(); | ||
context.closeIp = ip; | ||
debugBrackets(`${this.nodeId} closeBracket-C from '${context.source}' to ${context.ports}: '${ip.data}'`); | ||
debugBrackets("".concat(this.nodeId, " closeBracket-C from '").concat(context.source, "' to ").concat(context.ports, ": '").concat(ip.data, "'")); | ||
result = { | ||
@@ -418,5 +523,6 @@ __resolved: true, | ||
this.processOutputQueue(); | ||
} | ||
// Check if buffer contains data IPs. If it does, we want to allow | ||
} // Check if buffer contains data IPs. If it does, we want to allow | ||
// firing | ||
if (!dataPackets.length) { | ||
@@ -426,91 +532,125 @@ return; | ||
} | ||
} | ||
// Prepare the input/output pair | ||
} // Prepare the input/output pair | ||
context = new ProcessContext(ip, this, port, result); | ||
input = new ProcessInput(this.inPorts, context); | ||
output = new ProcessOutput(this.outPorts, context); | ||
var input = new ProcessInput(this.inPorts, context); | ||
var output = new ProcessOutput(this.outPorts, context); | ||
try { | ||
// Call the processing function | ||
this.handle(input, output, context); | ||
} catch (error1) { | ||
e = error1; | ||
} catch (e) { | ||
this.deactivate(context); | ||
output.sendDone(e); | ||
} | ||
if (context.activated) { | ||
return; | ||
} | ||
// If receiving an IP object didn't cause the component to | ||
} // If receiving an IP object didn't cause the component to | ||
// activate, log that input conditions were not met | ||
if (port.isAddressable()) { | ||
debug(`${this.nodeId} packet on '${port.name}[${ip.index}]' didn't match preconditions: ${ip.type}`); | ||
debug("".concat(this.nodeId, " packet on '").concat(port.name, "[").concat(ip.index, "]' didn't match preconditions: ").concat(ip.type)); | ||
return; | ||
} | ||
debug(`${this.nodeId} packet on '${port.name}' didn't match preconditions: ${ip.type}`); | ||
} | ||
// Get the current bracket forwarding context for an IP object | ||
getBracketContext(type, port, scope, idx) { | ||
var index, name, portsList; | ||
({name, index} = ports.normalizePortName(port)); | ||
debug("".concat(this.nodeId, " packet on '").concat(port.name, "' didn't match preconditions: ").concat(ip.type)); | ||
} // Get the current bracket forwarding context for an IP object | ||
}, { | ||
key: "getBracketContext", | ||
value: function getBracketContext(type, port, scope, idx) { | ||
var _ports$normalizePortN = ports.normalizePortName(port), | ||
name = _ports$normalizePortN.name, | ||
index = _ports$normalizePortN.index; | ||
if (idx != null) { | ||
index = idx; | ||
} | ||
portsList = type === 'in' ? this.inPorts : this.outPorts; | ||
var portsList = type === 'in' ? this.inPorts : this.outPorts; | ||
if (portsList[name].isAddressable()) { | ||
port = `${name}[${index}]`; | ||
name = "".concat(name, "[").concat(index, "]"); | ||
} else { | ||
name = port; | ||
} // Ensure we have a bracket context for the current scope | ||
if (!this.bracketContext[type][name]) { | ||
this.bracketContext[type][name] = {}; | ||
} | ||
if (!this.bracketContext[type][port]) { | ||
// Ensure we have a bracket context for the current scope | ||
this.bracketContext[type][port] = {}; | ||
if (!this.bracketContext[type][name][scope]) { | ||
this.bracketContext[type][name][scope] = []; | ||
} | ||
if (!this.bracketContext[type][port][scope]) { | ||
this.bracketContext[type][port][scope] = []; | ||
} | ||
return this.bracketContext[type][port][scope]; | ||
} | ||
// Add an IP object to the list of results to be sent in | ||
return this.bracketContext[type][name][scope]; | ||
} // Add an IP object to the list of results to be sent in | ||
// order | ||
addToResult(result, port, ip, before = false) { | ||
var idx, index, method, name; | ||
({name, index} = ports.normalizePortName(port)); | ||
method = before ? 'unshift' : 'push'; | ||
}, { | ||
key: "addToResult", | ||
value: function addToResult(result, port, packet) { | ||
var before = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; | ||
var res = result; | ||
var ip = packet; | ||
var _ports$normalizePortN2 = ports.normalizePortName(port), | ||
name = _ports$normalizePortN2.name, | ||
index = _ports$normalizePortN2.index; | ||
var method = before ? 'unshift' : 'push'; | ||
if (this.outPorts[name].isAddressable()) { | ||
idx = index ? parseInt(index) : ip.index; | ||
if (!result[name]) { | ||
result[name] = {}; | ||
var idx = index ? parseInt(index, 10) : ip.index; | ||
if (!res[name]) { | ||
res[name] = {}; | ||
} | ||
if (!result[name][idx]) { | ||
result[name][idx] = []; | ||
if (!res[name][idx]) { | ||
res[name][idx] = []; | ||
} | ||
ip.index = idx; | ||
result[name][idx][method](ip); | ||
res[name][idx][method](ip); | ||
return; | ||
} | ||
if (!result[name]) { | ||
result[name] = []; | ||
if (!res[name]) { | ||
res[name] = []; | ||
} | ||
return result[name][method](ip); | ||
} | ||
// Get contexts that can be forwarded with this in/outport | ||
res[name][method](ip); | ||
} // Get contexts that can be forwarded with this in/outport | ||
// pair. | ||
getForwardableContexts(inport, outport, contexts) { | ||
var forwardable, index, name; | ||
({name, index} = ports.normalizePortName(outport)); | ||
forwardable = []; | ||
contexts.forEach((ctx, idx) => { | ||
var outContext; | ||
}, { | ||
key: "getForwardableContexts", | ||
value: function getForwardableContexts(inport, outport, contexts) { | ||
var _this7 = this; | ||
var _ports$normalizePortN3 = ports.normalizePortName(outport), | ||
name = _ports$normalizePortN3.name, | ||
index = _ports$normalizePortN3.index; | ||
var forwardable = []; | ||
contexts.forEach(function (ctx, idx) { | ||
// No forwarding to this outport | ||
if (!this.isForwardingOutport(inport, name)) { | ||
if (!_this7.isForwardingOutport(inport, name)) { | ||
return; | ||
} | ||
// We have already forwarded this context to this outport | ||
} // We have already forwarded this context to this outport | ||
if (ctx.ports.indexOf(outport) !== -1) { | ||
return; | ||
} | ||
// See if we have already forwarded the same bracket from another | ||
} // See if we have already forwarded the same bracket from another | ||
// inport | ||
outContext = this.getBracketContext('out', name, ctx.ip.scope, index)[idx]; | ||
var outContext = _this7.getBracketContext('out', name, ctx.ip.scope, index)[idx]; | ||
if (outContext) { | ||
@@ -521,811 +661,275 @@ if (outContext.ip.data === ctx.ip.data && outContext.ports.indexOf(outport) !== -1) { | ||
} | ||
return forwardable.push(ctx); | ||
forwardable.push(ctx); | ||
}); | ||
return forwardable; | ||
} | ||
} // Add any bracket forwards needed to the result queue | ||
// Add any bracket forwards needed to the result queue | ||
addBracketForwards(result) { | ||
var context, i, ipClone, j, k, l, len1, len2, len3, len4, port, ref, ref1, ref2, ref3, ref4, ref5; | ||
if ((ref = result.__bracketClosingBefore) != null ? ref.length : void 0) { | ||
ref1 = result.__bracketClosingBefore; | ||
for (i = 0, len1 = ref1.length; i < len1; i++) { | ||
context = ref1[i]; | ||
debugBrackets(`${this.nodeId} closeBracket-A from '${context.source}' to ${context.ports}: '${context.closeIp.data}'`); | ||
}, { | ||
key: "addBracketForwards", | ||
value: function addBracketForwards(result) { | ||
var _this8 = this; | ||
var res = result; | ||
if (res.__bracketClosingBefore != null ? res.__bracketClosingBefore.length : undefined) { | ||
res.__bracketClosingBefore.forEach(function (context) { | ||
debugBrackets("".concat(_this8.nodeId, " closeBracket-A from '").concat(context.source, "' to ").concat(context.ports, ": '").concat(context.closeIp.data, "'")); | ||
if (!context.ports.length) { | ||
continue; | ||
return; | ||
} | ||
ref2 = context.ports; | ||
for (j = 0, len2 = ref2.length; j < len2; j++) { | ||
port = ref2[j]; | ||
ipClone = context.closeIp.clone(); | ||
this.addToResult(result, port, ipClone, true); | ||
this.getBracketContext('out', port, ipClone.scope).pop(); | ||
} | ||
} | ||
context.ports.forEach(function (port) { | ||
var ipClone = context.closeIp.clone(); | ||
_this8.addToResult(res, port, ipClone, true); | ||
_this8.getBracketContext('out', port, ipClone.scope).pop(); | ||
}); | ||
}); | ||
} | ||
if (result.__bracketContext) { | ||
if (res.__bracketContext) { | ||
// First see if there are any brackets to forward. We need to reverse | ||
// the keys so that they get added in correct order | ||
Object.keys(result.__bracketContext).reverse().forEach((inport) => { | ||
var ctx, datas, forwardedOpens, idx, idxIps, ip, ips, k, l, len3, len4, len5, m, outport, portIdentifier, results, unforwarded; | ||
context = result.__bracketContext[inport]; | ||
Object.keys(res.__bracketContext).reverse().forEach(function (inport) { | ||
var context = res.__bracketContext[inport]; | ||
if (!context.length) { | ||
return; | ||
} | ||
results = []; | ||
for (outport in result) { | ||
ips = result[outport]; | ||
Object.keys(res).forEach(function (outport) { | ||
var datas; | ||
var forwardedOpens; | ||
var unforwarded; | ||
var ips = res[outport]; | ||
if (outport.indexOf('__') === 0) { | ||
continue; | ||
return; | ||
} | ||
if (this.outPorts[outport].isAddressable()) { | ||
for (idx in ips) { | ||
idxIps = ips[idx]; | ||
if (_this8.outPorts[outport].isAddressable()) { | ||
Object.keys(ips).forEach(function (idx) { | ||
// Don't register indexes we're only sending brackets to | ||
datas = idxIps.filter(function(ip) { | ||
var idxIps = ips[idx]; | ||
datas = idxIps.filter(function (ip) { | ||
return ip.type === 'data'; | ||
}); | ||
if (!datas.length) { | ||
continue; | ||
return; | ||
} | ||
portIdentifier = `${outport}[${idx}]`; | ||
unforwarded = this.getForwardableContexts(inport, portIdentifier, context); | ||
var portIdentifier = "".concat(outport, "[").concat(idx, "]"); | ||
unforwarded = _this8.getForwardableContexts(inport, portIdentifier, context); | ||
if (!unforwarded.length) { | ||
continue; | ||
return; | ||
} | ||
forwardedOpens = []; | ||
for (k = 0, len3 = unforwarded.length; k < len3; k++) { | ||
ctx = unforwarded[k]; | ||
debugBrackets(`${this.nodeId} openBracket from '${inport}' to '${portIdentifier}': '${ctx.ip.data}'`); | ||
ipClone = ctx.ip.clone(); | ||
ipClone.index = parseInt(idx); | ||
unforwarded.forEach(function (ctx) { | ||
debugBrackets("".concat(_this8.nodeId, " openBracket from '").concat(inport, "' to '").concat(portIdentifier, "': '").concat(ctx.ip.data, "'")); | ||
var ipClone = ctx.ip.clone(); | ||
ipClone.index = parseInt(idx, 10); | ||
forwardedOpens.push(ipClone); | ||
ctx.ports.push(portIdentifier); | ||
this.getBracketContext('out', outport, ctx.ip.scope, idx).push(ctx); | ||
} | ||
_this8.getBracketContext('out', outport, ctx.ip.scope, idx).push(ctx); | ||
}); | ||
forwardedOpens.reverse(); | ||
for (l = 0, len4 = forwardedOpens.length; l < len4; l++) { | ||
ip = forwardedOpens[l]; | ||
this.addToResult(result, outport, ip, true); | ||
} | ||
} | ||
continue; | ||
} | ||
// Don't register ports we're only sending brackets to | ||
datas = ips.filter(function(ip) { | ||
forwardedOpens.forEach(function (ip) { | ||
_this8.addToResult(res, outport, ip, true); | ||
}); | ||
}); | ||
return; | ||
} // Don't register ports we're only sending brackets to | ||
datas = ips.filter(function (ip) { | ||
return ip.type === 'data'; | ||
}); | ||
if (!datas.length) { | ||
continue; | ||
return; | ||
} | ||
unforwarded = this.getForwardableContexts(inport, outport, context); | ||
unforwarded = _this8.getForwardableContexts(inport, outport, context); | ||
if (!unforwarded.length) { | ||
continue; | ||
return; | ||
} | ||
forwardedOpens = []; | ||
for (m = 0, len5 = unforwarded.length; m < len5; m++) { | ||
ctx = unforwarded[m]; | ||
debugBrackets(`${this.nodeId} openBracket from '${inport}' to '${outport}': '${ctx.ip.data}'`); | ||
unforwarded.forEach(function (ctx) { | ||
debugBrackets("".concat(_this8.nodeId, " openBracket from '").concat(inport, "' to '").concat(outport, "': '").concat(ctx.ip.data, "'")); | ||
forwardedOpens.push(ctx.ip.clone()); | ||
ctx.ports.push(outport); | ||
this.getBracketContext('out', outport, ctx.ip.scope).push(ctx); | ||
} | ||
_this8.getBracketContext('out', outport, ctx.ip.scope).push(ctx); | ||
}); | ||
forwardedOpens.reverse(); | ||
results.push((function() { | ||
var len6, n, results1; | ||
results1 = []; | ||
for (n = 0, len6 = forwardedOpens.length; n < len6; n++) { | ||
ip = forwardedOpens[n]; | ||
results1.push(this.addToResult(result, outport, ip, true)); | ||
} | ||
return results1; | ||
}).call(this)); | ||
} | ||
return results; | ||
forwardedOpens.forEach(function (ip) { | ||
_this8.addToResult(res, outport, ip, true); | ||
}); | ||
}); | ||
}); | ||
} | ||
if ((ref3 = result.__bracketClosingAfter) != null ? ref3.length : void 0) { | ||
ref4 = result.__bracketClosingAfter; | ||
for (k = 0, len3 = ref4.length; k < len3; k++) { | ||
context = ref4[k]; | ||
debugBrackets(`${this.nodeId} closeBracket-B from '${context.source}' to ${context.ports}: '${context.closeIp.data}'`); | ||
if (res.__bracketClosingAfter != null ? res.__bracketClosingAfter.length : undefined) { | ||
res.__bracketClosingAfter.forEach(function (context) { | ||
debugBrackets("".concat(_this8.nodeId, " closeBracket-B from '").concat(context.source, "' to ").concat(context.ports, ": '").concat(context.closeIp.data, "'")); | ||
if (!context.ports.length) { | ||
continue; | ||
return; | ||
} | ||
ref5 = context.ports; | ||
for (l = 0, len4 = ref5.length; l < len4; l++) { | ||
port = ref5[l]; | ||
ipClone = context.closeIp.clone(); | ||
this.addToResult(result, port, ipClone, false); | ||
this.getBracketContext('out', port, ipClone.scope).pop(); | ||
} | ||
} | ||
} | ||
delete result.__bracketClosingBefore; | ||
delete result.__bracketContext; | ||
return delete result.__bracketClosingAfter; | ||
} | ||
// Whenever an execution context finishes, send all resolved | ||
// output from the queue in the order it is in. | ||
processOutputQueue() { | ||
var idx, idxIps, ip, ips, port, portIdentifier, result, results; | ||
results = []; | ||
while (this.outputQ.length > 0) { | ||
if (!this.outputQ[0].__resolved) { | ||
break; | ||
} | ||
result = this.outputQ.shift(); | ||
this.addBracketForwards(result); | ||
results.push((function() { | ||
var i, len1, results1; | ||
results1 = []; | ||
for (port in result) { | ||
ips = result[port]; | ||
if (port.indexOf('__') === 0) { | ||
continue; | ||
} | ||
if (this.outPorts.ports[port].isAddressable()) { | ||
for (idx in ips) { | ||
idxIps = ips[idx]; | ||
idx = parseInt(idx); | ||
if (!this.outPorts.ports[port].isAttached(idx)) { | ||
continue; | ||
} | ||
for (i = 0, len1 = idxIps.length; i < len1; i++) { | ||
ip = idxIps[i]; | ||
portIdentifier = `${port}[${ip.index}]`; | ||
if (ip.type === 'openBracket') { | ||
debugSend(`${this.nodeId} sending ${portIdentifier} < '${ip.data}'`); | ||
} else if (ip.type === 'closeBracket') { | ||
debugSend(`${this.nodeId} sending ${portIdentifier} > '${ip.data}'`); | ||
} else { | ||
debugSend(`${this.nodeId} sending ${portIdentifier} DATA`); | ||
} | ||
if (!this.outPorts[port].options.scoped) { | ||
ip.scope = null; | ||
} | ||
this.outPorts[port].sendIP(ip); | ||
} | ||
} | ||
continue; | ||
} | ||
if (!this.outPorts.ports[port].isAttached()) { | ||
continue; | ||
} | ||
results1.push((function() { | ||
var j, len2, results2; | ||
results2 = []; | ||
for (j = 0, len2 = ips.length; j < len2; j++) { | ||
ip = ips[j]; | ||
portIdentifier = port; | ||
if (ip.type === 'openBracket') { | ||
debugSend(`${this.nodeId} sending ${portIdentifier} < '${ip.data}'`); | ||
} else if (ip.type === 'closeBracket') { | ||
debugSend(`${this.nodeId} sending ${portIdentifier} > '${ip.data}'`); | ||
} else { | ||
debugSend(`${this.nodeId} sending ${portIdentifier} DATA`); | ||
} | ||
if (!this.outPorts[port].options.scoped) { | ||
ip.scope = null; | ||
} | ||
results2.push(this.outPorts[port].sendIP(ip)); | ||
} | ||
return results2; | ||
}).call(this)); | ||
} | ||
return results1; | ||
}).call(this)); | ||
} | ||
return results; | ||
} | ||
context.ports.forEach(function (port) { | ||
var ipClone = context.closeIp.clone(); | ||
// Signal that component has activated. There may be multiple | ||
// activated contexts at the same time | ||
activate(context) { | ||
if (context.activated) { // prevent double activation | ||
return; | ||
} | ||
context.activated = true; | ||
context.deactivated = false; | ||
this.load++; | ||
this.emit('activate', this.load); | ||
if (this.ordered || this.autoOrdering) { | ||
return this.outputQ.push(context.result); | ||
} | ||
} | ||
_this8.addToResult(res, port, ipClone, false); | ||
// Signal that component has deactivated. There may be multiple | ||
// activated contexts at the same time | ||
deactivate(context) { | ||
if (context.deactivated) { // prevent double deactivation | ||
return; | ||
_this8.getBracketContext('out', port, ipClone.scope).pop(); | ||
}); | ||
}); | ||
} | ||
context.deactivated = true; | ||
context.activated = false; | ||
if (this.isOrdered()) { | ||
this.processOutputQueue(); | ||
} | ||
this.load--; | ||
return this.emit('deactivate', this.load); | ||
} | ||
}; | ||
delete res.__bracketClosingBefore; | ||
delete res.__bracketContext; | ||
delete res.__bracketClosingAfter; | ||
} // Whenever an execution context finishes, send all resolved | ||
// output from the queue in the order it is in. | ||
Component.prototype.description = ''; | ||
}, { | ||
key: "processOutputQueue", | ||
value: function processOutputQueue() { | ||
var _this9 = this; | ||
Component.prototype.icon = null; | ||
var _loop = function _loop() { | ||
if (!_this9.outputQ[0].__resolved) { | ||
return "break"; | ||
} | ||
return Component; | ||
var result = _this9.outputQ.shift(); | ||
}).call(this); | ||
_this9.addBracketForwards(result); | ||
ProcessContext = class ProcessContext { | ||
constructor(ip1, nodeInstance, port1, result1) { | ||
this.ip = ip1; | ||
this.nodeInstance = nodeInstance; | ||
this.port = port1; | ||
this.result = result1; | ||
this.scope = this.ip.scope; | ||
this.activated = false; | ||
this.deactivated = false; | ||
} | ||
Object.keys(result).forEach(function (port) { | ||
var portIdentifier; | ||
var ips = result[port]; | ||
activate() { | ||
// Push a new result value if previous has been sent already | ||
if (this.result.__resolved || this.nodeInstance.outputQ.indexOf(this.result) === -1) { | ||
this.result = {}; | ||
} | ||
return this.nodeInstance.activate(this); | ||
} | ||
if (port.indexOf('__') === 0) { | ||
return; | ||
} | ||
deactivate() { | ||
if (!this.result.__resolved) { | ||
this.result.__resolved = true; | ||
} | ||
return this.nodeInstance.deactivate(this); | ||
} | ||
if (_this9.outPorts.ports[port].isAddressable()) { | ||
Object.keys(ips).forEach(function (index) { | ||
var idxIps = ips[index]; | ||
var idx = parseInt(index, 10); | ||
}; | ||
if (!_this9.outPorts.ports[port].isAttached(idx)) { | ||
return; | ||
} | ||
ProcessInput = class ProcessInput { | ||
constructor(ports1, context1) { | ||
this.ports = ports1; | ||
this.context = context1; | ||
this.nodeInstance = this.context.nodeInstance; | ||
this.ip = this.context.ip; | ||
this.port = this.context.port; | ||
this.result = this.context.result; | ||
this.scope = this.context.scope; | ||
} | ||
idxIps.forEach(function (packet) { | ||
var ip = packet; | ||
portIdentifier = "".concat(port, "[").concat(ip.index, "]"); | ||
// When preconditions are met, set component state to `activated` | ||
activate() { | ||
if (this.context.activated) { | ||
return; | ||
} | ||
if (this.nodeInstance.isOrdered()) { | ||
// We're handling packets in order. Set the result as non-resolved | ||
// so that it can be send when the order comes up | ||
this.result.__resolved = false; | ||
} | ||
this.nodeInstance.activate(this.context); | ||
if (this.port.isAddressable()) { | ||
return debug(`${this.nodeInstance.nodeId} packet on '${this.port.name}[${this.ip.index}]' caused activation ${this.nodeInstance.load}: ${this.ip.type}`); | ||
} else { | ||
return debug(`${this.nodeInstance.nodeId} packet on '${this.port.name}' caused activation ${this.nodeInstance.load}: ${this.ip.type}`); | ||
} | ||
} | ||
if (ip.type === 'openBracket') { | ||
debugSend("".concat(_this9.nodeId, " sending ").concat(portIdentifier, " < '").concat(ip.data, "'")); | ||
} else if (ip.type === 'closeBracket') { | ||
debugSend("".concat(_this9.nodeId, " sending ").concat(portIdentifier, " > '").concat(ip.data, "'")); | ||
} else { | ||
debugSend("".concat(_this9.nodeId, " sending ").concat(portIdentifier, " DATA")); | ||
} | ||
// ## Connection listing | ||
// This allows components to check which input ports are attached. This is | ||
// useful mainly for addressable ports | ||
attached(...args) { | ||
var i, len1, port, res; | ||
if (!args.length) { | ||
args = ['in']; | ||
} | ||
res = []; | ||
for (i = 0, len1 = args.length; i < len1; i++) { | ||
port = args[i]; | ||
if (!this.ports[port]) { | ||
throw new Error(`Node ${this.nodeInstance.nodeId} has no port '${port}'`); | ||
} | ||
res.push(this.ports[port].listAttached()); | ||
} | ||
if (args.length === 1) { | ||
return res.pop(); | ||
} | ||
return res; | ||
} | ||
if (!_this9.outPorts[port].options.scoped) { | ||
ip.scope = null; | ||
} | ||
// ## Input preconditions | ||
// When the processing function is called, it can check if input buffers | ||
// contain the packets needed for the process to fire. | ||
// This precondition handling is done via the `has` and `hasStream` methods. | ||
_this9.outPorts[port].sendIP(ip); | ||
}); | ||
}); | ||
return; | ||
} | ||
// Returns true if a port (or ports joined by logical AND) has a new IP | ||
// Passing a validation callback as a last argument allows more selective | ||
// checking of packets. | ||
has(...args) { | ||
var i, len1, port, validate; | ||
if (!args.length) { | ||
args = ['in']; | ||
} | ||
if (typeof args[args.length - 1] === 'function') { | ||
validate = args.pop(); | ||
} else { | ||
validate = function() { | ||
return true; | ||
}; | ||
} | ||
for (i = 0, len1 = args.length; i < len1; i++) { | ||
port = args[i]; | ||
if (Array.isArray(port)) { | ||
if (!this.ports[port[0]]) { | ||
throw new Error(`Node ${this.nodeInstance.nodeId} has no port '${port[0]}'`); | ||
} | ||
if (!this.ports[port[0]].isAddressable()) { | ||
throw new Error(`Non-addressable ports, access must be with string ${port[0]}`); | ||
} | ||
if (!this.ports[port[0]].has(this.scope, port[1], validate)) { | ||
return false; | ||
} | ||
continue; | ||
} | ||
if (!this.ports[port]) { | ||
throw new Error(`Node ${this.nodeInstance.nodeId} has no port '${port}'`); | ||
} | ||
if (this.ports[port].isAddressable()) { | ||
throw new Error(`For addressable ports, access must be with array [${port}, idx]`); | ||
} | ||
if (!this.ports[port].has(this.scope, validate)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
// Returns true if the ports contain data packets | ||
hasData(...args) { | ||
if (!args.length) { | ||
args = ['in']; | ||
} | ||
args.push(function(ip) { | ||
return ip.type === 'data'; | ||
}); | ||
return this.has.apply(this, args); | ||
} | ||
// Returns true if a port has a complete stream in its input buffer. | ||
hasStream(...args) { | ||
var dataBrackets, hasData, i, len1, port, portBrackets, validate, validateStream; | ||
if (!args.length) { | ||
args = ['in']; | ||
} | ||
if (typeof args[args.length - 1] === 'function') { | ||
validateStream = args.pop(); | ||
} else { | ||
validateStream = function() { | ||
return true; | ||
}; | ||
} | ||
for (i = 0, len1 = args.length; i < len1; i++) { | ||
port = args[i]; | ||
portBrackets = []; | ||
dataBrackets = []; | ||
hasData = false; | ||
validate = function(ip) { | ||
if (ip.type === 'openBracket') { | ||
portBrackets.push(ip.data); | ||
return false; | ||
} | ||
if (ip.type === 'data') { | ||
// Run the stream validation callback | ||
hasData = validateStream(ip, portBrackets); | ||
if (!portBrackets.length) { | ||
// Data IP on its own is a valid stream | ||
return hasData; | ||
if (!_this9.outPorts.ports[port].isAttached()) { | ||
return; | ||
} | ||
// Otherwise we need to check for complete stream | ||
return false; | ||
} | ||
if (ip.type === 'closeBracket') { | ||
portBrackets.pop(); | ||
if (portBrackets.length) { | ||
return false; | ||
} | ||
if (!hasData) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
}; | ||
if (!this.has(port, validate)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
// ## Input processing | ||
ips.forEach(function (packet) { | ||
var ip = packet; | ||
portIdentifier = port; | ||
// Once preconditions have been met, the processing function can read from | ||
// the input buffers. Reading packets sets the component as "activated". | ||
if (ip.type === 'openBracket') { | ||
debugSend("".concat(_this9.nodeId, " sending ").concat(portIdentifier, " < '").concat(ip.data, "'")); | ||
} else if (ip.type === 'closeBracket') { | ||
debugSend("".concat(_this9.nodeId, " sending ").concat(portIdentifier, " > '").concat(ip.data, "'")); | ||
} else { | ||
debugSend("".concat(_this9.nodeId, " sending ").concat(portIdentifier, " DATA")); | ||
} | ||
// Fetches IP object(s) for port(s) | ||
get(...args) { | ||
var i, idx, ip, len1, port, portname, res; | ||
this.activate(); | ||
if (!args.length) { | ||
args = ['in']; | ||
} | ||
res = []; | ||
for (i = 0, len1 = args.length; i < len1; i++) { | ||
port = args[i]; | ||
if (Array.isArray(port)) { | ||
[portname, idx] = port; | ||
if (!this.ports[portname].isAddressable()) { | ||
throw new Error('Non-addressable ports, access must be with string portname'); | ||
} | ||
} else { | ||
portname = port; | ||
if (this.ports[portname].isAddressable()) { | ||
throw new Error('For addressable ports, access must be with array [portname, idx]'); | ||
} | ||
} | ||
if (this.nodeInstance.isForwardingInport(portname)) { | ||
ip = this.__getForForwarding(portname, idx); | ||
res.push(ip); | ||
continue; | ||
} | ||
ip = this.ports[portname].get(this.scope, idx); | ||
res.push(ip); | ||
} | ||
if (args.length === 1) { | ||
return res[0]; | ||
} else { | ||
return res; | ||
} | ||
} | ||
if (!_this9.outPorts[port].options.scoped) { | ||
ip.scope = null; | ||
} | ||
__getForForwarding(port, idx) { | ||
var context, dataIp, i, ip, len1, prefix; | ||
prefix = []; | ||
dataIp = null; | ||
while (true) { | ||
// Read next packet | ||
// Read IPs until we hit data | ||
ip = this.ports[port].get(this.scope, idx); | ||
if (!ip) { | ||
// Stop at the end of the buffer | ||
break; | ||
} | ||
if (ip.type === 'data') { | ||
// Hit the data IP, stop here | ||
dataIp = ip; | ||
break; | ||
} | ||
// Keep track of bracket closings and openings before | ||
prefix.push(ip); | ||
} | ||
// Forwarding brackets that came before data packet need to manipulate context | ||
// and be added to result so they can be forwarded correctly to ports that | ||
// need them | ||
for (i = 0, len1 = prefix.length; i < len1; i++) { | ||
ip = prefix[i]; | ||
if (ip.type === 'closeBracket') { | ||
if (!this.result.__bracketClosingBefore) { | ||
// Bracket closings before data should remove bracket context | ||
this.result.__bracketClosingBefore = []; | ||
} | ||
context = this.nodeInstance.getBracketContext('in', port, this.scope, idx).pop(); | ||
context.closeIp = ip; | ||
this.result.__bracketClosingBefore.push(context); | ||
continue; | ||
} | ||
if (ip.type === 'openBracket') { | ||
// Bracket openings need to go to bracket context | ||
this.nodeInstance.getBracketContext('in', port, this.scope, idx).push({ | ||
ip: ip, | ||
ports: [], | ||
source: port | ||
_this9.outPorts[port].sendIP(ip); | ||
}); | ||
}); | ||
continue; | ||
} | ||
} | ||
if (!this.result.__bracketContext) { | ||
// Add current bracket context to the result so that when we send | ||
// to ports we can also add the surrounding brackets | ||
this.result.__bracketContext = {}; | ||
} | ||
this.result.__bracketContext[port] = this.nodeInstance.getBracketContext('in', port, this.scope, idx).slice(0); | ||
// Bracket closings that were in buffer after the data packet need to | ||
// be added to result for done() to read them from | ||
return dataIp; | ||
} | ||
}; | ||
// Fetches `data` property of IP object(s) for given port(s) | ||
getData(...args) { | ||
var datas, i, len1, packet, port; | ||
if (!args.length) { | ||
args = ['in']; | ||
} | ||
datas = []; | ||
for (i = 0, len1 = args.length; i < len1; i++) { | ||
port = args[i]; | ||
packet = this.get(port); | ||
if (packet == null) { | ||
// we add the null packet to the array so when getting | ||
// multiple ports, if one is null we still return it | ||
// so the indexes are correct. | ||
datas.push(packet); | ||
continue; | ||
} | ||
while (packet.type !== 'data') { | ||
packet = this.get(port); | ||
if (!packet) { | ||
break; | ||
} | ||
} | ||
datas.push(packet.data); | ||
} | ||
if (args.length === 1) { | ||
return datas.pop(); | ||
} | ||
return datas; | ||
} | ||
while (this.outputQ.length > 0) { | ||
var _ret = _loop(); | ||
// Fetches a complete data stream from the buffer. | ||
getStream(...args) { | ||
var datas, hasData, i, ip, len1, port, portBrackets, portPackets; | ||
if (!args.length) { | ||
args = ['in']; | ||
} | ||
datas = []; | ||
for (i = 0, len1 = args.length; i < len1; i++) { | ||
port = args[i]; | ||
portBrackets = []; | ||
portPackets = []; | ||
hasData = false; | ||
ip = this.get(port); | ||
if (!ip) { | ||
datas.push(void 0); | ||
if (_ret === "break") break; | ||
} | ||
while (ip) { | ||
if (ip.type === 'openBracket') { | ||
if (!portBrackets.length) { | ||
// First openBracket in stream, drop previous | ||
portPackets = []; | ||
hasData = false; | ||
} | ||
portBrackets.push(ip.data); | ||
portPackets.push(ip); | ||
} | ||
if (ip.type === 'data') { | ||
portPackets.push(ip); | ||
hasData = true; | ||
if (!portBrackets.length) { | ||
// Unbracketed data packet is a valid stream | ||
break; | ||
} | ||
} | ||
if (ip.type === 'closeBracket') { | ||
portPackets.push(ip); | ||
portBrackets.pop(); | ||
if (hasData && !portBrackets.length) { | ||
// Last close bracket finishes stream if there was data inside | ||
break; | ||
} | ||
} | ||
ip = this.get(port); | ||
} | ||
datas.push(portPackets); | ||
} | ||
if (args.length === 1) { | ||
return datas.pop(); | ||
} | ||
return datas; | ||
} | ||
} // Signal that component has activated. There may be multiple | ||
// activated contexts at the same time | ||
}; | ||
}, { | ||
key: "activate", | ||
value: function activate(context) { | ||
if (context.activated) { | ||
return; | ||
} // prevent double activation | ||
ProcessOutput = class ProcessOutput { | ||
constructor(ports1, context1) { | ||
this.ports = ports1; | ||
this.context = context1; | ||
this.nodeInstance = this.context.nodeInstance; | ||
this.ip = this.context.ip; | ||
this.result = this.context.result; | ||
this.scope = this.context.scope; | ||
} | ||
// Checks if a value is an Error | ||
isError(err) { | ||
return err instanceof Error || Array.isArray(err) && err.length > 0 && err[0] instanceof Error; | ||
} | ||
context.activated = true; | ||
context.deactivated = false; | ||
this.load += 1; | ||
this.emit('activate', this.load); | ||
// Sends an error object | ||
error(err) { | ||
var e, i, j, len1, len2, multiple, results; | ||
multiple = Array.isArray(err); | ||
if (!multiple) { | ||
err = [err]; | ||
} | ||
if ('error' in this.ports && (this.ports.error.isAttached() || !this.ports.error.isRequired())) { | ||
if (multiple) { | ||
this.sendIP('error', new IP('openBracket')); | ||
if (this.ordered || this.autoOrdering) { | ||
this.outputQ.push(context.result); | ||
} | ||
for (i = 0, len1 = err.length; i < len1; i++) { | ||
e = err[i]; | ||
this.sendIP('error', e); | ||
} | ||
if (multiple) { | ||
return this.sendIP('error', new IP('closeBracket')); | ||
} | ||
} else { | ||
results = []; | ||
for (j = 0, len2 = err.length; j < len2; j++) { | ||
e = err[j]; | ||
throw e; | ||
} | ||
return results; | ||
} | ||
} | ||
} // Signal that component has deactivated. There may be multiple | ||
// activated contexts at the same time | ||
// Sends a single IP object to a port | ||
sendIP(port, packet) { | ||
var ip; | ||
if (!IP.isIP(packet)) { | ||
ip = new IP('data', packet); | ||
} else { | ||
ip = packet; | ||
} | ||
if (this.scope !== null && ip.scope === null) { | ||
ip.scope = this.scope; | ||
} | ||
if (this.nodeInstance.outPorts[port].isAddressable() && ip.index === null) { | ||
throw new Error('Sending packets to addressable ports requires specifying index'); | ||
} | ||
if (this.nodeInstance.isOrdered()) { | ||
this.nodeInstance.addToResult(this.result, port, ip); | ||
return; | ||
} | ||
if (!this.nodeInstance.outPorts[port].options.scoped) { | ||
ip.scope = null; | ||
} | ||
return this.nodeInstance.outPorts[port].sendIP(ip); | ||
} | ||
}, { | ||
key: "deactivate", | ||
value: function deactivate(context) { | ||
if (context.deactivated) { | ||
return; | ||
} // prevent double deactivation | ||
// Sends packets for each port as a key in the map | ||
// or sends Error or a list of Errors if passed such | ||
send(outputMap) { | ||
var componentPorts, i, len1, mapIsInPorts, packet, port, ref, results; | ||
if (this.isError(outputMap)) { | ||
return this.error(outputMap); | ||
} | ||
componentPorts = []; | ||
mapIsInPorts = false; | ||
ref = Object.keys(this.ports.ports); | ||
for (i = 0, len1 = ref.length; i < len1; i++) { | ||
port = ref[i]; | ||
if (port !== 'error' && port !== 'ports' && port !== '_callbacks') { | ||
componentPorts.push(port); | ||
} | ||
if (!mapIsInPorts && (outputMap != null) && typeof outputMap === 'object' && Object.keys(outputMap).indexOf(port) !== -1) { | ||
mapIsInPorts = true; | ||
} | ||
} | ||
if (componentPorts.length === 1 && !mapIsInPorts) { | ||
this.sendIP(componentPorts[0], outputMap); | ||
return; | ||
} | ||
if (componentPorts.length > 1 && !mapIsInPorts) { | ||
throw new Error('Port must be specified for sending output'); | ||
} | ||
results = []; | ||
for (port in outputMap) { | ||
packet = outputMap[port]; | ||
results.push(this.sendIP(port, packet)); | ||
} | ||
return results; | ||
} | ||
// Sends the argument via `send()` and marks activation as `done()` | ||
sendDone(outputMap) { | ||
this.send(outputMap); | ||
return this.done(); | ||
} | ||
context.deactivated = true; | ||
context.activated = false; | ||
// Makes a map-style component pass a result value to `out` | ||
// keeping all IP metadata received from `in`, | ||
// or modifying it if `options` is provided | ||
pass(data, options = {}) { | ||
var key, val; | ||
if (!('out' in this.ports)) { | ||
throw new Error('output.pass() requires port "out" to be present'); | ||
} | ||
for (key in options) { | ||
val = options[key]; | ||
this.ip[key] = val; | ||
} | ||
this.ip.data = data; | ||
this.sendIP('out', this.ip); | ||
return this.done(); | ||
} | ||
if (this.isOrdered()) { | ||
this.processOutputQueue(); | ||
} | ||
// Finishes process activation gracefully | ||
done(error) { | ||
var buf, context, contexts, ctx, ip, isLast, nodeContext, port, ref; | ||
this.result.__resolved = true; | ||
this.nodeInstance.activate(this.context); | ||
if (error) { | ||
this.error(error); | ||
this.load -= 1; | ||
this.emit('deactivate', this.load); | ||
} | ||
isLast = () => { | ||
var len, load, pos, resultsOnly; | ||
// We only care about real output sets with processing data | ||
resultsOnly = this.nodeInstance.outputQ.filter(function(q) { | ||
if (!q.__resolved) { | ||
return true; | ||
} | ||
if (Object.keys(q).length === 2 && q.__bracketClosingAfter) { | ||
return false; | ||
} | ||
return true; | ||
}); | ||
pos = resultsOnly.indexOf(this.result); | ||
len = resultsOnly.length; | ||
load = this.nodeInstance.load; | ||
if (pos === len - 1) { | ||
return true; | ||
} | ||
if (pos === -1 && load === len + 1) { | ||
return true; | ||
} | ||
if (len <= 1 && load === 1) { | ||
return true; | ||
} | ||
return false; | ||
}; | ||
if (this.nodeInstance.isOrdered() && isLast()) { | ||
ref = this.nodeInstance.bracketContext.in; | ||
// We're doing bracket forwarding. See if there are | ||
// dangling closeBrackets in buffer since we're the | ||
// last running process function. | ||
for (port in ref) { | ||
contexts = ref[port]; | ||
if (!contexts[this.scope]) { | ||
continue; | ||
} | ||
nodeContext = contexts[this.scope]; | ||
if (!nodeContext.length) { | ||
continue; | ||
} | ||
context = nodeContext[nodeContext.length - 1]; | ||
buf = this.nodeInstance.inPorts[context.source].getBuffer(context.ip.scope, context.ip.index); | ||
while (true) { | ||
if (!buf.length) { | ||
break; | ||
} | ||
if (buf[0].type !== 'closeBracket') { | ||
break; | ||
} | ||
ip = this.nodeInstance.inPorts[context.source].get(context.ip.scope, context.ip.index); | ||
ctx = nodeContext.pop(); | ||
ctx.closeIp = ip; | ||
if (!this.result.__bracketClosingAfter) { | ||
this.result.__bracketClosingAfter = []; | ||
} | ||
this.result.__bracketClosingAfter.push(ctx); | ||
} | ||
} | ||
} | ||
debug(`${this.nodeInstance.nodeId} finished processing ${this.nodeInstance.load}`); | ||
return this.nodeInstance.deactivate(this.context); | ||
} | ||
}]); | ||
}; | ||
return Component; | ||
}(EventEmitter); | ||
exports.Component = Component; | ||
Component.description = ''; | ||
Component.icon = null; | ||
exports.Component = Component; |
@@ -0,1 +1,37 @@ | ||
"use strict"; | ||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } | ||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } | ||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } | ||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } | ||
function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } | ||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } | ||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } | ||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } | ||
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } | ||
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } | ||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } | ||
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } | ||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } | ||
// NoFlo - Flow-Based Programming for JavaScript | ||
@@ -5,18 +41,20 @@ // (c) 2013-2017 Flowhub UG | ||
// NoFlo may be freely distributed under the MIT license | ||
var ComponentLoader, EventEmitter, fbpGraph, platform, registerLoader; | ||
fbpGraph = require('fbp-graph'); | ||
/* eslint-disable | ||
class-methods-use-this, | ||
import/no-unresolved, | ||
*/ | ||
var fbpGraph = require('fbp-graph'); | ||
({EventEmitter} = require('events')); | ||
var _require = require('events'), | ||
EventEmitter = _require.EventEmitter; | ||
registerLoader = require('./loader/register'); | ||
var registerLoader = require('./loader/register'); | ||
platform = require('./Platform'); | ||
// ## The NoFlo Component Loader | ||
var platform = require('./Platform'); // ## The NoFlo Component Loader | ||
// | ||
// The Component Loader is responsible for discovering components | ||
// available in the running system, as well as for instantiating | ||
// them. | ||
// | ||
// Internally the loader uses a registered, platform-specific | ||
@@ -29,329 +67,428 @@ // loader. NoFlo ships with a loader for Node.js that discovers | ||
// loader using the [noflo-component-loader](https://github.com/noflo/noflo-component-loader) webpack plugin. | ||
ComponentLoader = class ComponentLoader extends EventEmitter { | ||
constructor(baseDir, options = {}) { | ||
super(); | ||
this.baseDir = baseDir; | ||
this.options = options; | ||
this.components = null; | ||
this.libraryIcons = {}; | ||
this.processing = false; | ||
this.ready = false; | ||
this.setMaxListeners(0); | ||
} | ||
// Get the library prefix for a given module name. This | ||
var ComponentLoader = /*#__PURE__*/function (_EventEmitter) { | ||
_inherits(ComponentLoader, _EventEmitter); | ||
var _super = _createSuper(ComponentLoader); | ||
function ComponentLoader(baseDir) { | ||
var _this; | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
_classCallCheck(this, ComponentLoader); | ||
_this = _super.call(this); | ||
_this.baseDir = baseDir; | ||
_this.options = options; | ||
_this.components = null; | ||
_this.libraryIcons = {}; | ||
_this.processing = false; | ||
_this.ready = false; | ||
_this.setMaxListeners(0); | ||
return _this; | ||
} // Get the library prefix for a given module name. This | ||
// is mostly used for generating valid names for namespaced | ||
// NPM modules, as well as for convenience renaming all | ||
// `noflo-` prefixed modules with just their base name. | ||
// | ||
// Examples: | ||
// | ||
// * `my-project` becomes `my-project` | ||
// * `@foo/my-project` becomes `my-project` | ||
// * `noflo-core` becomes `core` | ||
getModulePrefix(name) { | ||
if (!name) { | ||
return ''; | ||
} | ||
if (name === 'noflo') { | ||
return ''; | ||
} | ||
if (name[0] === '@') { | ||
name = name.replace(/\@[a-z\-]+\//, ''); | ||
} | ||
return name.replace(/^noflo-/, ''); | ||
} | ||
// Get the list of all available components | ||
listComponents(callback) { | ||
if (this.processing) { | ||
this.once('ready', () => { | ||
return callback(null, this.components); | ||
_createClass(ComponentLoader, [{ | ||
key: "getModulePrefix", | ||
value: function getModulePrefix(name) { | ||
if (!name) { | ||
return ''; | ||
} | ||
var res = name; | ||
if (res === 'noflo') { | ||
return ''; | ||
} | ||
if (res[0] === '@') { | ||
res = res.replace(/@[a-z-]+\//, ''); | ||
} | ||
return res.replace(/^noflo-/, ''); | ||
} // Get the list of all available components | ||
}, { | ||
key: "listComponents", | ||
value: function listComponents(callback) { | ||
var _this2 = this; | ||
if (this.processing) { | ||
this.once('ready', function () { | ||
return callback(null, _this2.components); | ||
}); | ||
return; | ||
} | ||
if (this.components) { | ||
callback(null, this.components); | ||
return; | ||
} | ||
this.ready = false; | ||
this.processing = true; | ||
this.components = {}; | ||
registerLoader.register(this, function (err) { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
_this2.processing = false; | ||
_this2.ready = true; | ||
_this2.emit('ready', true); | ||
callback(null, _this2.components); | ||
}); | ||
return; | ||
} | ||
if (this.components) { | ||
return callback(null, this.components); | ||
} | ||
this.ready = false; | ||
this.processing = true; | ||
this.components = {}; | ||
registerLoader.register(this, (err) => { | ||
if (err) { | ||
if (callback) { | ||
return callback(err); | ||
} // Load an instance of a specific component. If the | ||
// registered component is a JSON or FBP graph, it will | ||
// be loaded as an instance of the NoFlo subgraph | ||
// component. | ||
}, { | ||
key: "load", | ||
value: function load(name, callback, metadata) { | ||
var _this3 = this; | ||
if (!this.ready) { | ||
this.listComponents(function (err) { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
_this3.load(name, callback, metadata); | ||
}); | ||
return; | ||
} | ||
var component = this.components[name]; | ||
if (!component) { | ||
// Try an alias | ||
var keys = Object.keys(this.components); | ||
for (var i = 0; i < keys.length; i += 1) { | ||
var componentName = keys[i]; | ||
if (componentName.split('/')[1] === name) { | ||
component = this.components[componentName]; | ||
break; | ||
} | ||
} | ||
throw err; | ||
if (!component) { | ||
// Failure to load | ||
callback(new Error("Component ".concat(name, " not available with base ").concat(this.baseDir))); | ||
return; | ||
} | ||
} | ||
this.processing = false; | ||
this.ready = true; | ||
this.emit('ready', true); | ||
if (callback) { | ||
return callback(null, this.components); | ||
if (this.isGraph(component)) { | ||
this.loadGraph(name, component, callback, metadata); | ||
return; | ||
} | ||
}); | ||
} | ||
// Load an instance of a specific component. If the | ||
// registered component is a JSON or FBP graph, it will | ||
// be loaded as an instance of the NoFlo subgraph | ||
// component. | ||
load(name, callback, metadata) { | ||
var component, componentName; | ||
if (!this.ready) { | ||
this.listComponents((err) => { | ||
this.createComponent(name, component, metadata, function (err, instance) { | ||
var inst = instance; | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
return this.load(name, callback, metadata); | ||
if (!inst) { | ||
callback(new Error("Component ".concat(name, " could not be loaded."))); | ||
return; | ||
} | ||
if (name === 'Graph') { | ||
inst.baseDir = _this3.baseDir; | ||
} | ||
if (typeof name === 'string') { | ||
inst.componentName = name; | ||
} | ||
if (inst.isLegacy()) { | ||
platform.deprecated("Component ".concat(name, " uses legacy NoFlo APIs. Please port to Process API")); | ||
} | ||
_this3.setIcon(name, inst); | ||
callback(null, inst); | ||
}); | ||
return; | ||
} | ||
component = this.components[name]; | ||
if (!component) { | ||
// Try an alias | ||
for (componentName in this.components) { | ||
if (componentName.split('/')[1] === name) { | ||
component = this.components[componentName]; | ||
break; | ||
} // Creates an instance of a component. | ||
}, { | ||
key: "createComponent", | ||
value: function createComponent(name, component, metadata, callback) { | ||
var e; | ||
var instance; | ||
var implementation = component; | ||
if (!implementation) { | ||
callback(new Error("Component ".concat(name, " not available"))); | ||
return; | ||
} // If a string was specified, attempt to `require` it. | ||
if (typeof implementation === 'string') { | ||
if (typeof registerLoader.dynamicLoad === 'function') { | ||
registerLoader.dynamicLoad(name, implementation, metadata, callback); | ||
return; | ||
} | ||
} | ||
if (!component) { | ||
// Failure to load | ||
callback(new Error(`Component ${name} not available with base ${this.baseDir}`)); | ||
callback(Error("Dynamic loading of ".concat(implementation, " for component ").concat(name, " not available on this platform."))); | ||
return; | ||
} | ||
} | ||
if (this.isGraph(component)) { | ||
if (!platform.isBrowser()) { | ||
// nextTick is faster on Node.js | ||
process.nextTick(() => { | ||
return this.loadGraph(name, component, callback, metadata); | ||
}); | ||
} // Attempt to create the component instance using the `getComponent` method. | ||
if (typeof implementation.getComponent === 'function') { | ||
try { | ||
instance = implementation.getComponent(metadata); | ||
} catch (error) { | ||
e = error; | ||
callback(e); | ||
return; | ||
} // Attempt to create a component using a factory function. | ||
} else if (typeof implementation === 'function') { | ||
try { | ||
instance = implementation(metadata); | ||
} catch (error1) { | ||
e = error1; | ||
callback(e); | ||
return; | ||
} | ||
} else { | ||
setTimeout(() => { | ||
return this.loadGraph(name, component, callback, metadata); | ||
}, 0); | ||
} | ||
return; | ||
} | ||
return this.createComponent(name, component, metadata, (err, instance) => { | ||
if (err) { | ||
return callback(err); | ||
} | ||
if (!instance) { | ||
callback(new Error(`Component ${name} could not be loaded.`)); | ||
callback(new Error("Invalid type ".concat(_typeof(implementation), " for component ").concat(name, "."))); | ||
return; | ||
} | ||
if (name === 'Graph') { | ||
instance.baseDir = this.baseDir; | ||
callback(null, instance); | ||
} // Check if a given filesystem path is actually a graph | ||
}, { | ||
key: "isGraph", | ||
value: function isGraph(cPath) { | ||
// Live graph instance | ||
if (_typeof(cPath) === 'object' && cPath instanceof fbpGraph.Graph) { | ||
return true; | ||
} // Graph JSON definition | ||
if (_typeof(cPath) === 'object' && cPath.processes && cPath.connections) { | ||
return true; | ||
} | ||
if (typeof name === 'string') { | ||
instance.componentName = name; | ||
} | ||
if (instance.isLegacy()) { | ||
platform.deprecated(`Component ${name} uses legacy NoFlo APIs. Please port to Process API`); | ||
} | ||
this.setIcon(name, instance); | ||
return callback(null, instance); | ||
}); | ||
} | ||
// Creates an instance of a component. | ||
createComponent(name, component, metadata, callback) { | ||
var e, implementation, instance; | ||
implementation = component; | ||
if (!implementation) { | ||
return callback(new Error(`Component ${name} not available`)); | ||
} | ||
// If a string was specified, attempt to `require` it. | ||
if (typeof implementation === 'string') { | ||
if (typeof registerLoader.dynamicLoad === 'function') { | ||
registerLoader.dynamicLoad(name, implementation, metadata, callback); | ||
if (typeof cPath !== 'string') { | ||
return false; | ||
} // Graph file path | ||
return cPath.indexOf('.fbp') !== -1 || cPath.indexOf('.json') !== -1; | ||
} // Load a graph as a NoFlo subgraph component instance | ||
}, { | ||
key: "loadGraph", | ||
value: function loadGraph(name, component, callback, metadata) { | ||
var _this4 = this; | ||
this.createComponent(name, this.components.Graph, metadata, function (error, graph) { | ||
var g = graph; | ||
if (error) { | ||
callback(error); | ||
return; | ||
} | ||
g.loader = _this4; | ||
g.baseDir = _this4.baseDir; | ||
g.inPorts.remove('graph'); | ||
g.setGraph(component, function (err) { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
_this4.setIcon(name, g); | ||
callback(null, g); | ||
}); | ||
}); | ||
} // Set icon for the component instance. If the instance | ||
// has an icon set, then this is a no-op. Otherwise we | ||
// determine an icon based on the module it is coming | ||
// from, or use a fallback icon separately for subgraphs | ||
// and elementary components. | ||
}, { | ||
key: "setIcon", | ||
value: function setIcon(name, instance) { | ||
// See if component has an icon | ||
if (!instance.getIcon || instance.getIcon()) { | ||
return; | ||
} // See if library has an icon | ||
var _name$split = name.split('/'), | ||
_name$split2 = _slicedToArray(_name$split, 2), | ||
library = _name$split2[0], | ||
componentName = _name$split2[1]; | ||
if (componentName && this.getLibraryIcon(library)) { | ||
instance.setIcon(this.getLibraryIcon(library)); | ||
return; | ||
} // See if instance is a subgraph | ||
if (instance.isSubgraph()) { | ||
instance.setIcon('sitemap'); | ||
return; | ||
} | ||
return callback(Error(`Dynamic loading of ${implementation} for component ${name} not available on this platform.`)); | ||
instance.setIcon('gear'); | ||
} | ||
// Attempt to create the component instance using the `getComponent` method. | ||
if (typeof implementation.getComponent === 'function') { | ||
try { | ||
instance = implementation.getComponent(metadata); | ||
} catch (error) { | ||
e = error; | ||
return callback(e); | ||
}, { | ||
key: "getLibraryIcon", | ||
value: function getLibraryIcon(prefix) { | ||
if (this.libraryIcons[prefix]) { | ||
return this.libraryIcons[prefix]; | ||
} | ||
// Attempt to create a component using a factory function. | ||
} else if (typeof implementation === 'function') { | ||
try { | ||
instance = implementation(metadata); | ||
} catch (error) { | ||
e = error; | ||
return callback(e); | ||
} | ||
} else { | ||
callback(new Error(`Invalid type ${typeof implementation} for component ${name}.`)); | ||
return; | ||
} | ||
return callback(null, instance); | ||
} | ||
// Check if a given filesystem path is actually a graph | ||
isGraph(cPath) { | ||
if (typeof cPath === 'object' && cPath instanceof fbpGraph.Graph) { | ||
// Live graph instance | ||
return true; | ||
return null; | ||
} | ||
if (typeof cPath === 'object' && cPath.processes && cPath.connections) { | ||
// Graph JSON definition | ||
return true; | ||
}, { | ||
key: "setLibraryIcon", | ||
value: function setLibraryIcon(prefix, icon) { | ||
this.libraryIcons[prefix] = icon; | ||
} | ||
if (typeof cPath !== 'string') { | ||
return false; | ||
} | ||
// Graph file path | ||
return cPath.indexOf('.fbp') !== -1 || cPath.indexOf('.json') !== -1; | ||
} | ||
}, { | ||
key: "normalizeName", | ||
value: function normalizeName(packageId, name) { | ||
var prefix = this.getModulePrefix(packageId); | ||
var fullName = "".concat(prefix, "/").concat(name); | ||
// Load a graph as a NoFlo subgraph component instance | ||
loadGraph(name, component, callback, metadata) { | ||
this.createComponent(name, this.components['Graph'], metadata, (err, graph) => { | ||
if (err) { | ||
return callback(err); | ||
if (!packageId) { | ||
fullName = name; | ||
} | ||
graph.loader = this; | ||
graph.baseDir = this.baseDir; | ||
graph.inPorts.remove('graph'); | ||
graph.setGraph(component, (err) => { | ||
if (err) { | ||
return callback(err); | ||
} | ||
this.setIcon(name, graph); | ||
return callback(null, graph); | ||
}); | ||
}); | ||
} | ||
// Set icon for the component instance. If the instance | ||
// has an icon set, then this is a no-op. Otherwise we | ||
// determine an icon based on the module it is coming | ||
// from, or use a fallback icon separately for subgraphs | ||
// and elementary components. | ||
setIcon(name, instance) { | ||
var componentName, library; | ||
// See if component has an icon | ||
if (!instance.getIcon || instance.getIcon()) { | ||
return; | ||
} | ||
// See if library has an icon | ||
[library, componentName] = name.split('/'); | ||
if (componentName && this.getLibraryIcon(library)) { | ||
instance.setIcon(this.getLibraryIcon(library)); | ||
return; | ||
} | ||
// See if instance is a subgraph | ||
if (instance.isSubgraph()) { | ||
instance.setIcon('sitemap'); | ||
return; | ||
} | ||
instance.setIcon('gear'); | ||
} | ||
return fullName; | ||
} // ### Registering components at runtime | ||
// | ||
// In addition to components discovered by the loader, | ||
// it is possible to register components at runtime. | ||
// | ||
// With the `registerComponent` method you can register | ||
// a NoFlo Component constructor or factory method | ||
// as a component available for loading. | ||
getLibraryIcon(prefix) { | ||
if (this.libraryIcons[prefix]) { | ||
return this.libraryIcons[prefix]; | ||
} | ||
return null; | ||
} | ||
}, { | ||
key: "registerComponent", | ||
value: function registerComponent(packageId, name, cPath, callback) { | ||
var fullName = this.normalizeName(packageId, name); | ||
this.components[fullName] = cPath; | ||
setLibraryIcon(prefix, icon) { | ||
return this.libraryIcons[prefix] = icon; | ||
} | ||
if (callback) { | ||
callback(); | ||
} | ||
} // With the `registerGraph` method you can register new | ||
// graphs as loadable components. | ||
normalizeName(packageId, name) { | ||
var fullName, prefix; | ||
prefix = this.getModulePrefix(packageId); | ||
fullName = `${prefix}/${name}`; | ||
if (!packageId) { | ||
fullName = name; | ||
} | ||
return fullName; | ||
} | ||
}, { | ||
key: "registerGraph", | ||
value: function registerGraph(packageId, name, gPath, callback) { | ||
this.registerComponent(packageId, name, gPath, callback); | ||
} // With `registerLoader` you can register custom component | ||
// loaders. They will be called immediately and can register | ||
// any components or graphs they wish. | ||
// ### Registering components at runtime | ||
}, { | ||
key: "registerLoader", | ||
value: function registerLoader(loader, callback) { | ||
loader(this, callback); | ||
} // With `setSource` you can register a component by providing | ||
// a source code string. Supported languages and techniques | ||
// depend on the runtime environment, for example CoffeeScript | ||
// components can only be registered via `setSource` if | ||
// the environment has a CoffeeScript compiler loaded. | ||
// In addition to components discovered by the loader, | ||
// it is possible to register components at runtime. | ||
}, { | ||
key: "setSource", | ||
value: function setSource(packageId, name, source, language, callback) { | ||
var _this5 = this; | ||
// With the `registerComponent` method you can register | ||
// a NoFlo Component constructor or factory method | ||
// as a component available for loading. | ||
registerComponent(packageId, name, cPath, callback) { | ||
var fullName; | ||
fullName = this.normalizeName(packageId, name); | ||
this.components[fullName] = cPath; | ||
if (callback) { | ||
return callback(); | ||
} | ||
} | ||
if (!registerLoader.setSource) { | ||
callback(new Error('setSource not allowed')); | ||
return; | ||
} | ||
// With the `registerGraph` method you can register new | ||
// graphs as loadable components. | ||
registerGraph(packageId, name, gPath, callback) { | ||
return this.registerComponent(packageId, name, gPath, callback); | ||
} | ||
if (!this.ready) { | ||
this.listComponents(function (err) { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
// With `registerLoader` you can register custom component | ||
// loaders. They will be called immediately and can register | ||
// any components or graphs they wish. | ||
registerLoader(loader, callback) { | ||
return loader(this, callback); | ||
} | ||
_this5.setSource(packageId, name, source, language, callback); | ||
}); | ||
return; | ||
} | ||
// With `setSource` you can register a component by providing | ||
// a source code string. Supported languages and techniques | ||
// depend on the runtime environment, for example CoffeeScript | ||
// components can only be registered via `setSource` if | ||
// the environment has a CoffeeScript compiler loaded. | ||
setSource(packageId, name, source, language, callback) { | ||
if (!registerLoader.setSource) { | ||
return callback(new Error('setSource not allowed')); | ||
} | ||
if (!this.ready) { | ||
this.listComponents((err) => { | ||
if (err) { | ||
return callback(err); | ||
} | ||
return this.setSource(packageId, name, source, language, callback); | ||
}); | ||
return; | ||
} | ||
return registerLoader.setSource(this, packageId, name, source, language, callback); | ||
} | ||
registerLoader.setSource(this, packageId, name, source, language, callback); | ||
} // `getSource` allows fetching the source code of a registered | ||
// component as a string. | ||
// `getSource` allows fetching the source code of a registered | ||
// component as a string. | ||
getSource(name, callback) { | ||
if (!registerLoader.getSource) { | ||
return callback(new Error('getSource not allowed')); | ||
}, { | ||
key: "getSource", | ||
value: function getSource(name, callback) { | ||
var _this6 = this; | ||
if (!registerLoader.getSource) { | ||
callback(new Error('getSource not allowed')); | ||
return; | ||
} | ||
if (!this.ready) { | ||
this.listComponents(function (err) { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
_this6.getSource(name, callback); | ||
}); | ||
return; | ||
} | ||
registerLoader.getSource(this, name, callback); | ||
} | ||
if (!this.ready) { | ||
this.listComponents((err) => { | ||
if (err) { | ||
return callback(err); | ||
} | ||
return this.getSource(name, callback); | ||
}); | ||
return; | ||
}, { | ||
key: "clear", | ||
value: function clear() { | ||
this.components = null; | ||
this.ready = false; | ||
this.processing = false; | ||
} | ||
return registerLoader.getSource(this, name, callback); | ||
} | ||
}]); | ||
clear() { | ||
this.components = null; | ||
this.ready = false; | ||
return this.processing = false; | ||
} | ||
return ComponentLoader; | ||
}(EventEmitter); | ||
}; | ||
exports.ComponentLoader = ComponentLoader; | ||
exports.ComponentLoader = ComponentLoader; |
@@ -0,274 +1,383 @@ | ||
"use strict"; | ||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } | ||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } | ||
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } | ||
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } | ||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } | ||
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } | ||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } | ||
// NoFlo - Flow-Based Programming for JavaScript | ||
// (c) 2014-2017 Flowhub UG | ||
// NoFlo may be freely distributed under the MIT license | ||
var BasePort, IP, InPort; | ||
var BasePort = require('./BasePort'); // ## NoFlo inport | ||
// | ||
// Input Port (inport) implementation for NoFlo components. These | ||
// ports are the way a component receives Information Packets. | ||
BasePort = require('./BasePort'); | ||
IP = require('./IP'); | ||
module.exports = /*#__PURE__*/function (_BasePort) { | ||
_inherits(InPort, _BasePort); | ||
// ## NoFlo inport | ||
var _super = _createSuper(InPort); | ||
// Input Port (inport) implementation for NoFlo components. These | ||
// ports are the way a component receives Information Packets. | ||
InPort = class InPort extends BasePort { | ||
constructor(options = {}) { | ||
if (options.control == null) { | ||
options.control = false; | ||
function InPort() { | ||
var _this; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
_classCallCheck(this, InPort); | ||
var opts = options; | ||
if (opts.control == null) { | ||
opts.control = false; | ||
} | ||
if (options.scoped == null) { | ||
options.scoped = true; | ||
if (opts.scoped == null) { | ||
opts.scoped = true; | ||
} | ||
if (options.triggering == null) { | ||
options.triggering = true; | ||
if (opts.triggering == null) { | ||
opts.triggering = true; | ||
} | ||
if (options.process) { | ||
if (opts.process) { | ||
throw new Error('InPort process callback is deprecated. Please use Process API'); | ||
} | ||
if (options.handle) { | ||
if (opts.handle) { | ||
throw new Error('InPort handle callback is deprecated. Please use Process API'); | ||
} | ||
super(options); | ||
this.prepareBuffer(); | ||
} | ||
// Assign a delegate for retrieving data should this inPort | ||
attachSocket(socket, localId = null) { | ||
// have a default value. | ||
if (this.hasDefault()) { | ||
socket.setDataDelegate(() => { | ||
return this.options.default; | ||
_this = _super.call(this, opts); | ||
_this.prepareBuffer(); | ||
return _this; | ||
} // Assign a delegate for retrieving data should this inPort | ||
_createClass(InPort, [{ | ||
key: "attachSocket", | ||
value: function attachSocket(socket) { | ||
var _this2 = this; | ||
var localId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; | ||
// have a default value. | ||
if (this.hasDefault()) { | ||
socket.setDataDelegate(function () { | ||
return _this2.options["default"]; | ||
}); | ||
} | ||
socket.on('connect', function () { | ||
return _this2.handleSocketEvent('connect', socket, localId); | ||
}); | ||
socket.on('begingroup', function (group) { | ||
return _this2.handleSocketEvent('begingroup', group, localId); | ||
}); | ||
socket.on('data', function (data) { | ||
_this2.validateData(data); | ||
return _this2.handleSocketEvent('data', data, localId); | ||
}); | ||
socket.on('endgroup', function (group) { | ||
return _this2.handleSocketEvent('endgroup', group, localId); | ||
}); | ||
socket.on('disconnect', function () { | ||
return _this2.handleSocketEvent('disconnect', socket, localId); | ||
}); | ||
socket.on('ip', function (ip) { | ||
return _this2.handleIP(ip, localId); | ||
}); | ||
} | ||
socket.on('connect', () => { | ||
return this.handleSocketEvent('connect', socket, localId); | ||
}); | ||
socket.on('begingroup', (group) => { | ||
return this.handleSocketEvent('begingroup', group, localId); | ||
}); | ||
socket.on('data', (data) => { | ||
this.validateData(data); | ||
return this.handleSocketEvent('data', data, localId); | ||
}); | ||
socket.on('endgroup', (group) => { | ||
return this.handleSocketEvent('endgroup', group, localId); | ||
}); | ||
socket.on('disconnect', () => { | ||
return this.handleSocketEvent('disconnect', socket, localId); | ||
}); | ||
return socket.on('ip', (ip) => { | ||
return this.handleIP(ip, localId); | ||
}); | ||
} | ||
}, { | ||
key: "handleIP", | ||
value: function handleIP(packet, index) { | ||
if (this.options.control && packet.type !== 'data') { | ||
return; | ||
} | ||
handleIP(ip, id) { | ||
var buf; | ||
if (this.options.control && ip.type !== 'data') { | ||
return; | ||
var ip = packet; | ||
ip.owner = this.nodeInstance; | ||
if (this.isAddressable()) { | ||
ip.index = index; | ||
} | ||
if (ip.datatype === 'all') { | ||
// Stamp non-specific IP objects with port datatype | ||
ip.datatype = this.getDataType(); | ||
} | ||
if (this.getSchema() && !ip.schema) { | ||
// Stamp non-specific IP objects with port schema | ||
ip.schema = this.getSchema(); | ||
} | ||
var buf = this.prepareBufferForIP(ip); | ||
buf.push(ip); | ||
if (this.options.control && buf.length > 1) { | ||
buf.shift(); | ||
} | ||
this.emit('ip', ip, index); | ||
} | ||
ip.owner = this.nodeInstance; | ||
if (this.isAddressable()) { | ||
ip.index = id; | ||
}, { | ||
key: "handleSocketEvent", | ||
value: function handleSocketEvent(event, payload, id) { | ||
// Emit port event | ||
if (this.isAddressable()) { | ||
return this.emit(event, payload, id); | ||
} | ||
return this.emit(event, payload); | ||
} | ||
if (ip.datatype === 'all') { | ||
// Stamp non-specific IP objects with port datatype | ||
ip.datatype = this.getDataType(); | ||
}, { | ||
key: "hasDefault", | ||
value: function hasDefault() { | ||
return this.options["default"] !== undefined; | ||
} | ||
if (this.getSchema() && !ip.schema) { | ||
// Stamp non-specific IP objects with port schema | ||
ip.schema = this.getSchema(); | ||
} | ||
buf = this.prepareBufferForIP(ip); | ||
buf.push(ip); | ||
if (this.options.control && buf.length > 1) { | ||
buf.shift(); | ||
} | ||
return this.emit('ip', ip, id); | ||
} | ||
}, { | ||
key: "prepareBuffer", | ||
value: function prepareBuffer() { | ||
if (this.isAddressable()) { | ||
if (this.options.scoped) { | ||
this.scopedBuffer = {}; | ||
} | ||
handleSocketEvent(event, payload, id) { | ||
if (this.isAddressable()) { | ||
// Emit port event | ||
return this.emit(event, payload, id); | ||
} | ||
return this.emit(event, payload); | ||
} | ||
this.indexedBuffer = {}; | ||
this.iipBuffer = {}; | ||
return; | ||
} | ||
hasDefault() { | ||
return this.options.default !== void 0; | ||
} | ||
prepareBuffer() { | ||
if (this.isAddressable()) { | ||
if (this.options.scoped) { | ||
this.scopedBuffer = {}; | ||
} | ||
this.indexedBuffer = {}; | ||
this.iipBuffer = {}; | ||
return; | ||
this.iipBuffer = []; | ||
this.buffer = []; | ||
} | ||
if (this.options.scoped) { | ||
this.scopedBuffer = {}; | ||
} | ||
this.iipBuffer = []; | ||
this.buffer = []; | ||
} | ||
}, { | ||
key: "prepareBufferForIP", | ||
value: function prepareBufferForIP(ip) { | ||
if (this.isAddressable()) { | ||
if (ip.scope != null && this.options.scoped) { | ||
if (!(ip.scope in this.scopedBuffer)) { | ||
this.scopedBuffer[ip.scope] = []; | ||
} | ||
prepareBufferForIP(ip) { | ||
if (this.isAddressable()) { | ||
if ((ip.scope != null) && this.options.scoped) { | ||
if (!(ip.index in this.scopedBuffer[ip.scope])) { | ||
this.scopedBuffer[ip.scope][ip.index] = []; | ||
} | ||
return this.scopedBuffer[ip.scope][ip.index]; | ||
} | ||
if (ip.initial) { | ||
if (!(ip.index in this.iipBuffer)) { | ||
this.iipBuffer[ip.index] = []; | ||
} | ||
return this.iipBuffer[ip.index]; | ||
} | ||
if (!(ip.index in this.indexedBuffer)) { | ||
this.indexedBuffer[ip.index] = []; | ||
} | ||
return this.indexedBuffer[ip.index]; | ||
} | ||
if (ip.scope != null && this.options.scoped) { | ||
if (!(ip.scope in this.scopedBuffer)) { | ||
this.scopedBuffer[ip.scope] = []; | ||
} | ||
if (!(ip.index in this.scopedBuffer[ip.scope])) { | ||
this.scopedBuffer[ip.scope][ip.index] = []; | ||
} | ||
return this.scopedBuffer[ip.scope][ip.index]; | ||
return this.scopedBuffer[ip.scope]; | ||
} | ||
if (ip.initial) { | ||
if (!(ip.index in this.iipBuffer)) { | ||
this.iipBuffer[ip.index] = []; | ||
} | ||
return this.iipBuffer[ip.index]; | ||
return this.iipBuffer; | ||
} | ||
if (!(ip.index in this.indexedBuffer)) { | ||
this.indexedBuffer[ip.index] = []; | ||
} | ||
return this.indexedBuffer[ip.index]; | ||
return this.buffer; | ||
} | ||
if ((ip.scope != null) && this.options.scoped) { | ||
if (!(ip.scope in this.scopedBuffer)) { | ||
this.scopedBuffer[ip.scope] = []; | ||
}, { | ||
key: "validateData", | ||
value: function validateData(data) { | ||
if (!this.options.values) { | ||
return; | ||
} | ||
return this.scopedBuffer[ip.scope]; | ||
} | ||
if (ip.initial) { | ||
return this.iipBuffer; | ||
} | ||
return this.buffer; | ||
} | ||
validateData(data) { | ||
if (!this.options.values) { | ||
return; | ||
if (this.options.values.indexOf(data) === -1) { | ||
throw new Error("Invalid data='".concat(data, "' received, not in [").concat(this.options.values, "]")); | ||
} | ||
} | ||
if (this.options.values.indexOf(data) === -1) { | ||
throw new Error(`Invalid data='${data}' received, not in [${this.options.values}]`); | ||
} | ||
} | ||
}, { | ||
key: "getBuffer", | ||
value: function getBuffer(scope, index) { | ||
var initial = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; | ||
getBuffer(scope, idx, initial = false) { | ||
if (this.isAddressable()) { | ||
if ((scope != null) && this.options.scoped) { | ||
if (!(scope in this.scopedBuffer)) { | ||
return void 0; | ||
if (this.isAddressable()) { | ||
if (scope != null && this.options.scoped) { | ||
if (!(scope in this.scopedBuffer)) { | ||
return undefined; | ||
} | ||
if (!(index in this.scopedBuffer[scope])) { | ||
return undefined; | ||
} | ||
return this.scopedBuffer[scope][index]; | ||
} | ||
if (!(idx in this.scopedBuffer[scope])) { | ||
return void 0; | ||
if (initial) { | ||
if (!(index in this.iipBuffer)) { | ||
return undefined; | ||
} | ||
return this.iipBuffer[index]; | ||
} | ||
return this.scopedBuffer[scope][idx]; | ||
if (!(index in this.indexedBuffer)) { | ||
return undefined; | ||
} | ||
return this.indexedBuffer[index]; | ||
} | ||
if (initial) { | ||
if (!(idx in this.iipBuffer)) { | ||
return void 0; | ||
if (scope != null && this.options.scoped) { | ||
if (!(scope in this.scopedBuffer)) { | ||
return undefined; | ||
} | ||
return this.iipBuffer[idx]; | ||
return this.scopedBuffer[scope]; | ||
} | ||
if (!(idx in this.indexedBuffer)) { | ||
return void 0; | ||
if (initial) { | ||
return this.iipBuffer; | ||
} | ||
return this.indexedBuffer[idx]; | ||
return this.buffer; | ||
} | ||
if ((scope != null) && this.options.scoped) { | ||
if (!(scope in this.scopedBuffer)) { | ||
return void 0; | ||
}, { | ||
key: "getFromBuffer", | ||
value: function getFromBuffer(scope, index) { | ||
var initial = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; | ||
var buf = this.getBuffer(scope, index, initial); | ||
if (!(buf != null ? buf.length : undefined)) { | ||
return undefined; | ||
} | ||
return this.scopedBuffer[scope]; | ||
} | ||
if (initial) { | ||
return this.iipBuffer; | ||
} | ||
return this.buffer; | ||
} | ||
getFromBuffer(scope, idx, initial = false) { | ||
var buf; | ||
buf = this.getBuffer(scope, idx, initial); | ||
if (!(buf != null ? buf.length : void 0)) { | ||
return void 0; | ||
} | ||
if (this.options.control) { | ||
return buf[buf.length - 1]; | ||
} else { | ||
if (this.options.control) { | ||
return buf[buf.length - 1]; | ||
} | ||
return buf.shift(); | ||
} | ||
} | ||
} // Fetches a packet from the port | ||
// Fetches a packet from the port | ||
get(scope, idx) { | ||
var res; | ||
res = this.getFromBuffer(scope, idx); | ||
if (res !== void 0) { | ||
return res; | ||
}, { | ||
key: "get", | ||
value: function get(scope, index) { | ||
var res = this.getFromBuffer(scope, index); | ||
if (res !== undefined) { | ||
return res; | ||
} // Try to find an IIP instead | ||
return this.getFromBuffer(null, index, true); | ||
} | ||
// Try to find an IIP instead | ||
return this.getFromBuffer(null, idx, true); | ||
} | ||
}, { | ||
key: "hasIPinBuffer", | ||
value: function hasIPinBuffer(scope, index, validate) { | ||
var initial = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; | ||
var buf = this.getBuffer(scope, index, initial); | ||
hasIPinBuffer(scope, idx, validate, initial = false) { | ||
var buf, i, len, packet; | ||
buf = this.getBuffer(scope, idx, initial); | ||
if (!(buf != null ? buf.length : void 0)) { | ||
if (!(buf != null ? buf.length : undefined)) { | ||
return false; | ||
} | ||
for (var i = 0; i < buf.length; i += 1) { | ||
if (validate(buf[i])) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
for (i = 0, len = buf.length; i < len; i++) { | ||
packet = buf[i]; | ||
if (validate(packet)) { | ||
}, { | ||
key: "hasIIP", | ||
value: function hasIIP(index, validate) { | ||
return this.hasIPinBuffer(null, index, validate, true); | ||
} // Returns true if port contains packet(s) matching the validator | ||
}, { | ||
key: "has", | ||
value: function has(scope, index, validate) { | ||
var valid = validate; | ||
var idx = index; | ||
if (!this.isAddressable()) { | ||
valid = idx; | ||
idx = null; | ||
} | ||
if (this.hasIPinBuffer(scope, idx, valid)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
hasIIP(idx, validate) { | ||
return this.hasIPinBuffer(null, idx, validate, true); | ||
} | ||
if (this.hasIIP(idx, valid)) { | ||
return true; | ||
} | ||
// Returns true if port contains packet(s) matching the validator | ||
has(scope, idx, validate) { | ||
if (!this.isAddressable()) { | ||
validate = idx; | ||
idx = null; | ||
} | ||
if (this.hasIPinBuffer(scope, idx, validate)) { | ||
return true; | ||
} | ||
if (this.hasIIP(idx, validate)) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
return false; | ||
} // Returns the number of data packets in an inport | ||
// Returns the number of data packets in an inport | ||
length(scope, idx) { | ||
var buf; | ||
buf = this.getBuffer(scope, idx); | ||
if (!buf) { | ||
return 0; | ||
} | ||
return buf.length; | ||
} | ||
}, { | ||
key: "length", | ||
value: function length(scope, index) { | ||
var buf = this.getBuffer(scope, index); | ||
// Tells if buffer has packets or not | ||
ready(scope, idx) { | ||
return this.length(scope) > 0; | ||
} | ||
if (!buf) { | ||
return 0; | ||
} | ||
// Clears inport buffers | ||
clear() { | ||
return this.prepareBuffer(); | ||
} | ||
return buf.length; | ||
} // Tells if buffer has packets or not | ||
}; | ||
}, { | ||
key: "ready", | ||
value: function ready(scope) { | ||
return this.length(scope) > 0; | ||
} // Clears inport buffers | ||
module.exports = InPort; | ||
}, { | ||
key: "clear", | ||
value: function clear() { | ||
return this.prepareBuffer(); | ||
} | ||
}]); | ||
return InPort; | ||
}(BasePort); |
@@ -0,1 +1,25 @@ | ||
"use strict"; | ||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } | ||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } | ||
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } | ||
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } | ||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } | ||
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } | ||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } | ||
// NoFlo - Flow-Based Programming for JavaScript | ||
@@ -5,10 +29,54 @@ // (c) 2013-2017 Flowhub UG | ||
// NoFlo may be freely distributed under the MIT license | ||
var EventEmitter, IP, InternalSocket; | ||
var _require = require('events'), | ||
EventEmitter = _require.EventEmitter; | ||
({EventEmitter} = require('events')); | ||
var IP = require('./IP'); | ||
IP = require('./IP'); | ||
function legacyToIp(event, payload) { | ||
// No need to wrap modern IP Objects | ||
if (IP.isIP(payload)) { | ||
return payload; | ||
} // Wrap legacy events into appropriate IP objects | ||
// ## Internal Sockets | ||
switch (event) { | ||
case 'begingroup': | ||
return new IP('openBracket', payload); | ||
case 'endgroup': | ||
return new IP('closeBracket'); | ||
case 'data': | ||
return new IP('data', payload); | ||
default: | ||
return null; | ||
} | ||
} | ||
function ipToLegacy(ip) { | ||
switch (ip.type) { | ||
case 'openBracket': | ||
return { | ||
event: 'begingroup', | ||
payload: ip.data | ||
}; | ||
case 'data': | ||
return { | ||
event: 'data', | ||
payload: ip.data | ||
}; | ||
case 'closeBracket': | ||
return { | ||
event: 'endgroup', | ||
payload: ip.data | ||
}; | ||
default: | ||
return null; | ||
} | ||
} // ## Internal Sockets | ||
// | ||
// The default communications mechanism between NoFlo processes is | ||
@@ -19,44 +87,60 @@ // an _internal socket_, which is responsible for accepting information | ||
// connected process. | ||
InternalSocket = class InternalSocket extends EventEmitter { | ||
regularEmitEvent(event, data) { | ||
return this.emit(event, data); | ||
} | ||
debugEmitEvent(event, data) { | ||
var error; | ||
try { | ||
return this.emit(event, data); | ||
} catch (error1) { | ||
error = error1; | ||
if (error.id && error.metadata && error.error) { | ||
if (this.listeners('error').length === 0) { | ||
var InternalSocket = /*#__PURE__*/function (_EventEmitter) { | ||
_inherits(InternalSocket, _EventEmitter); | ||
var _super = _createSuper(InternalSocket); | ||
_createClass(InternalSocket, [{ | ||
key: "regularEmitEvent", | ||
value: function regularEmitEvent(event, data) { | ||
this.emit(event, data); | ||
} | ||
}, { | ||
key: "debugEmitEvent", | ||
value: function debugEmitEvent(event, data) { | ||
try { | ||
this.emit(event, data); | ||
} catch (error) { | ||
if (error.id && error.metadata && error.error) { | ||
// Wrapped debuggable error coming from downstream, no need to wrap | ||
throw error.error; | ||
if (this.listeners('error').length === 0) { | ||
throw error.error; | ||
} | ||
this.emit('error', error); | ||
return; | ||
} | ||
this.emit('error', error); | ||
return; | ||
if (this.listeners('error').length === 0) { | ||
throw error; | ||
} | ||
this.emit('error', { | ||
id: this.to.process.id, | ||
error: error, | ||
metadata: this.metadata | ||
}); | ||
} | ||
if (this.listeners('error').length === 0) { | ||
throw error; | ||
} | ||
return this.emit('error', { | ||
id: this.to.process.id, | ||
error: error, | ||
metadata: this.metadata | ||
}); | ||
} | ||
} | ||
}]); | ||
constructor(metadata = {}) { | ||
super(); | ||
this.metadata = metadata; | ||
this.brackets = []; | ||
this.connected = false; | ||
this.dataDelegate = null; | ||
this.debug = false; | ||
this.emitEvent = this.regularEmitEvent; | ||
} | ||
function InternalSocket() { | ||
var _this; | ||
// ## Socket connections | ||
var metadata = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
_classCallCheck(this, InternalSocket); | ||
_this = _super.call(this); | ||
_this.metadata = metadata; | ||
_this.brackets = []; | ||
_this.connected = false; | ||
_this.dataDelegate = null; | ||
_this.debug = false; | ||
_this.emitEvent = _this.regularEmitEvent; | ||
return _this; | ||
} // ## Socket connections | ||
// | ||
// Sockets that are attached to the ports of processes may be | ||
@@ -66,9 +150,9 @@ // either connected or disconnected. The semantical meaning of | ||
// data. Disconnecting means an end of transmission. | ||
// | ||
// This can be used for example to signal the beginning and end | ||
// of information packets resulting from the reading of a single | ||
// file or a database query. | ||
// | ||
// Example, disconnecting when a file has been completely read: | ||
// | ||
// readBuffer: (fd, position, size, buffer) -> | ||
@@ -80,248 +164,255 @@ // fs.read fd, buffer, 0, buffer.length, position, (err, bytes, buffer) => | ||
// position += buffer.length | ||
// | ||
// # Disconnect when the file has been completely read | ||
// return @outPorts.out.disconnect() if position >= size | ||
// | ||
// # Otherwise, call same method recursively | ||
// @readBuffer fd, position, size, buffer | ||
connect() { | ||
if (this.connected) { | ||
return; | ||
_createClass(InternalSocket, [{ | ||
key: "connect", | ||
value: function connect() { | ||
if (this.connected) { | ||
return; | ||
} | ||
this.connected = true; | ||
this.emitEvent('connect', null); | ||
} | ||
this.connected = true; | ||
return this.emitEvent('connect', null); | ||
} | ||
}, { | ||
key: "disconnect", | ||
value: function disconnect() { | ||
if (!this.connected) { | ||
return; | ||
} | ||
disconnect() { | ||
if (!this.connected) { | ||
return; | ||
this.connected = false; | ||
this.emitEvent('disconnect', null); | ||
} | ||
this.connected = false; | ||
return this.emitEvent('disconnect', null); | ||
} | ||
}, { | ||
key: "isConnected", | ||
value: function isConnected() { | ||
return this.connected; | ||
} // ## Sending information packets | ||
// | ||
// The _send_ method is used by a processe's outport to | ||
// send information packets. The actual packet contents are | ||
// not defined by NoFlo, and may be any valid JavaScript data | ||
// structure. | ||
// | ||
// The packet contents however should be such that may be safely | ||
// serialized or deserialized via JSON. This way the NoFlo networks | ||
// can be constructed with more flexibility, as file buffers or | ||
// message queues can be used as additional packet relay mechanisms. | ||
isConnected() { | ||
return this.connected; | ||
} | ||
}, { | ||
key: "send", | ||
value: function send(data) { | ||
if (data === undefined && typeof this.dataDelegate === 'function') { | ||
this.handleSocketEvent('data', this.dataDelegate()); | ||
return; | ||
} | ||
// ## Sending information packets | ||
this.handleSocketEvent('data', data); | ||
} // ## Sending information packets without open bracket | ||
// | ||
// As _connect_ event is considered as open bracket, it needs to be followed | ||
// by a _disconnect_ event or a closing bracket. In the new simplified | ||
// sending semantics single IP objects can be sent without open/close brackets. | ||
// The _send_ method is used by a processe's outport to | ||
// send information packets. The actual packet contents are | ||
// not defined by NoFlo, and may be any valid JavaScript data | ||
// structure. | ||
}, { | ||
key: "post", | ||
value: function post(packet) { | ||
var autoDisconnect = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; | ||
var ip = packet; | ||
// The packet contents however should be such that may be safely | ||
// serialized or deserialized via JSON. This way the NoFlo networks | ||
// can be constructed with more flexibility, as file buffers or | ||
// message queues can be used as additional packet relay mechanisms. | ||
send(data) { | ||
if (data === void 0 && typeof this.dataDelegate === 'function') { | ||
data = this.dataDelegate(); | ||
} | ||
return this.handleSocketEvent('data', data); | ||
} | ||
if (ip === undefined && typeof this.dataDelegate === 'function') { | ||
ip = this.dataDelegate(); | ||
} // Send legacy connect/disconnect if needed | ||
// ## Sending information packets without open bracket | ||
// As _connect_ event is considered as open bracket, it needs to be followed | ||
// by a _disconnect_ event or a closing bracket. In the new simplified | ||
// sending semantics single IP objects can be sent without open/close brackets. | ||
post(ip, autoDisconnect = true) { | ||
if (ip === void 0 && typeof this.dataDelegate === 'function') { | ||
ip = this.dataDelegate(); | ||
} | ||
// Send legacy connect/disconnect if needed | ||
if (!this.isConnected() && this.brackets.length === 0) { | ||
this.connect(); | ||
} | ||
this.handleSocketEvent('ip', ip, false); | ||
if (autoDisconnect && this.isConnected() && this.brackets.length === 0) { | ||
return this.disconnect(); | ||
} | ||
} | ||
if (!this.isConnected() && this.brackets.length === 0) { | ||
this.connect(); | ||
} | ||
// ## Information Packet grouping | ||
this.handleSocketEvent('ip', ip, false); | ||
// Processes sending data to sockets may also group the packets | ||
// when necessary. This allows transmitting tree structures as | ||
// a stream of packets. | ||
if (autoDisconnect && this.isConnected() && this.brackets.length === 0) { | ||
this.disconnect(); | ||
} | ||
} // ## Information Packet grouping | ||
// | ||
// Processes sending data to sockets may also group the packets | ||
// when necessary. This allows transmitting tree structures as | ||
// a stream of packets. | ||
// | ||
// For example, an object could be split into multiple packets | ||
// where each property is identified by a separate grouping: | ||
// | ||
// # Group by object ID | ||
// @outPorts.out.beginGroup object.id | ||
// | ||
// for property, value of object | ||
// @outPorts.out.beginGroup property | ||
// @outPorts.out.send value | ||
// @outPorts.out.endGroup() | ||
// | ||
// @outPorts.out.endGroup() | ||
// | ||
// This would cause a tree structure to be sent to the receiving | ||
// process as a stream of packets. So, an article object may be | ||
// as packets like: | ||
// | ||
// * `/<article id>/title/Lorem ipsum` | ||
// * `/<article id>/author/Henri Bergius` | ||
// | ||
// Components are free to ignore groupings, but are recommended | ||
// to pass received groupings onward if the data structures remain | ||
// intact through the component's processing. | ||
// For example, an object could be split into multiple packets | ||
// where each property is identified by a separate grouping: | ||
}, { | ||
key: "beginGroup", | ||
value: function beginGroup(group) { | ||
this.handleSocketEvent('begingroup', group); | ||
} | ||
}, { | ||
key: "endGroup", | ||
value: function endGroup() { | ||
this.handleSocketEvent('endgroup'); | ||
} // ## Socket data delegation | ||
// | ||
// Sockets have the option to receive data from a delegate function | ||
// should the `send` method receive undefined for `data`. This | ||
// helps in the case of defaulting values. | ||
// # Group by object ID | ||
// @outPorts.out.beginGroup object.id | ||
}, { | ||
key: "setDataDelegate", | ||
value: function setDataDelegate(delegate) { | ||
if (typeof delegate !== 'function') { | ||
throw Error('A data delegate must be a function.'); | ||
} | ||
// for property, value of object | ||
// @outPorts.out.beginGroup property | ||
// @outPorts.out.send value | ||
// @outPorts.out.endGroup() | ||
this.dataDelegate = delegate; | ||
} // ## Socket debug mode | ||
// | ||
// Sockets can catch exceptions happening in processes when data is | ||
// sent to them. These errors can then be reported to the network for | ||
// notification to the developer. | ||
// @outPorts.out.endGroup() | ||
}, { | ||
key: "setDebug", | ||
value: function setDebug(active) { | ||
this.debug = active; | ||
this.emitEvent = this.debug ? this.debugEmitEvent : this.regularEmitEvent; | ||
} // ## Socket identifiers | ||
// | ||
// Socket identifiers are mainly used for debugging purposes. | ||
// Typical identifiers look like _ReadFile:OUT -> Display:IN_, | ||
// but for sockets sending initial information packets to | ||
// components may also loom like _DATA -> ReadFile:SOURCE_. | ||
// This would cause a tree structure to be sent to the receiving | ||
// process as a stream of packets. So, an article object may be | ||
// as packets like: | ||
}, { | ||
key: "getId", | ||
value: function getId() { | ||
var fromStr = function fromStr(from) { | ||
return "".concat(from.process.id, "() ").concat(from.port.toUpperCase()); | ||
}; | ||
// * `/<article id>/title/Lorem ipsum` | ||
// * `/<article id>/author/Henri Bergius` | ||
var toStr = function toStr(to) { | ||
return "".concat(to.port.toUpperCase(), " ").concat(to.process.id, "()"); | ||
}; | ||
// Components are free to ignore groupings, but are recommended | ||
// to pass received groupings onward if the data structures remain | ||
// intact through the component's processing. | ||
beginGroup(group) { | ||
return this.handleSocketEvent('begingroup', group); | ||
} | ||
if (!this.from && !this.to) { | ||
return 'UNDEFINED'; | ||
} | ||
endGroup() { | ||
return this.handleSocketEvent('endgroup'); | ||
} | ||
if (this.from && !this.to) { | ||
return "".concat(fromStr(this.from), " -> ANON"); | ||
} | ||
// ## Socket data delegation | ||
if (!this.from) { | ||
return "DATA -> ".concat(toStr(this.to)); | ||
} | ||
// Sockets have the option to receive data from a delegate function | ||
// should the `send` method receive undefined for `data`. This | ||
// helps in the case of defaulting values. | ||
setDataDelegate(delegate) { | ||
if (typeof delegate !== 'function') { | ||
throw Error('A data delegate must be a function.'); | ||
return "".concat(fromStr(this.from), " -> ").concat(toStr(this.to)); | ||
} | ||
return this.dataDelegate = delegate; | ||
} | ||
/* eslint-disable no-param-reassign */ | ||
// ## Socket debug mode | ||
}, { | ||
key: "handleSocketEvent", | ||
value: function handleSocketEvent(event, payload) { | ||
var autoConnect = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; | ||
var isIP = event === 'ip' && IP.isIP(payload); | ||
var ip = isIP ? payload : legacyToIp(event, payload); | ||
// Sockets can catch exceptions happening in processes when data is | ||
// sent to them. These errors can then be reported to the network for | ||
// notification to the developer. | ||
setDebug(active) { | ||
this.debug = active; | ||
return this.emitEvent = this.debug ? this.debugEmitEvent : this.regularEmitEvent; | ||
} | ||
if (!ip) { | ||
return; | ||
} | ||
// ## Socket identifiers | ||
if (!this.isConnected() && autoConnect && this.brackets.length === 0) { | ||
// Connect before sending | ||
this.connect(); | ||
} | ||
// Socket identifiers are mainly used for debugging purposes. | ||
// Typical identifiers look like _ReadFile:OUT -> Display:IN_, | ||
// but for sockets sending initial information packets to | ||
// components may also loom like _DATA -> ReadFile:SOURCE_. | ||
getId() { | ||
var fromStr, toStr; | ||
fromStr = function(from) { | ||
return `${from.process.id}() ${from.port.toUpperCase()}`; | ||
}; | ||
toStr = function(to) { | ||
return `${to.port.toUpperCase()} ${to.process.id}()`; | ||
}; | ||
if (!(this.from || this.to)) { | ||
return "UNDEFINED"; | ||
} | ||
if (this.from && !this.to) { | ||
return `${fromStr(this.from)} -> ANON`; | ||
} | ||
if (!this.from) { | ||
return `DATA -> ${toStr(this.to)}`; | ||
} | ||
return `${fromStr(this.from)} -> ${toStr(this.to)}`; | ||
} | ||
if (event === 'begingroup') { | ||
this.brackets.push(payload); | ||
} | ||
legacyToIp(event, payload) { | ||
if (IP.isIP(payload)) { | ||
// No need to wrap modern IP Objects | ||
return payload; | ||
} | ||
// Wrap legacy events into appropriate IP objects | ||
switch (event) { | ||
case 'begingroup': | ||
return new IP('openBracket', payload); | ||
case 'endgroup': | ||
return new IP('closeBracket'); | ||
case 'data': | ||
return new IP('data', payload); | ||
default: | ||
return null; | ||
} | ||
} | ||
if (isIP && ip.type === 'openBracket') { | ||
this.brackets.push(ip.data); | ||
} | ||
ipToLegacy(ip) { | ||
var legacy; | ||
switch (ip.type) { | ||
case 'openBracket': | ||
return legacy = { | ||
event: 'begingroup', | ||
payload: ip.data | ||
}; | ||
case 'data': | ||
return legacy = { | ||
event: 'data', | ||
payload: ip.data | ||
}; | ||
case 'closeBracket': | ||
return legacy = { | ||
event: 'endgroup', | ||
payload: ip.data | ||
}; | ||
} | ||
} | ||
if (event === 'endgroup') { | ||
// Prevent closing already closed groups | ||
if (this.brackets.length === 0) { | ||
return; | ||
} // Add group name to bracket | ||
handleSocketEvent(event, payload, autoConnect = true) { | ||
var ip, isIP, legacy; | ||
isIP = event === 'ip' && IP.isIP(payload); | ||
ip = isIP ? payload : this.legacyToIp(event, payload); | ||
if (!ip) { | ||
return; | ||
} | ||
if (!this.isConnected() && autoConnect && this.brackets.length === 0) { | ||
// Connect before sending | ||
this.connect(); | ||
} | ||
if (event === 'begingroup') { | ||
this.brackets.push(payload); | ||
} | ||
if (isIP && ip.type === 'openBracket') { | ||
this.brackets.push(ip.data); | ||
} | ||
if (event === 'endgroup') { | ||
// Prevent closing already closed groups | ||
if (this.brackets.length === 0) { | ||
return; | ||
ip.data = this.brackets.pop(); | ||
payload = ip.data; | ||
} | ||
// Add group name to bracket | ||
ip.data = this.brackets.pop(); | ||
payload = ip.data; | ||
} | ||
if (isIP && payload.type === 'closeBracket') { | ||
// Prevent closing already closed brackets | ||
if (this.brackets.length === 0) { | ||
if (isIP && payload.type === 'closeBracket') { | ||
// Prevent closing already closed brackets | ||
if (this.brackets.length === 0) { | ||
return; | ||
} | ||
this.brackets.pop(); | ||
} // Emit the IP Object | ||
this.emitEvent('ip', ip); // Emit the legacy event | ||
if (!ip || !ip.type) { | ||
return; | ||
} | ||
this.brackets.pop(); | ||
if (isIP) { | ||
var legacy = ipToLegacy(ip); | ||
event = legacy.event; | ||
payload = legacy.payload; | ||
} | ||
if (event === 'connect') { | ||
this.connected = true; | ||
} | ||
if (event === 'disconnect') { | ||
this.connected = false; | ||
} | ||
this.emitEvent(event, payload); | ||
} | ||
// Emit the IP Object | ||
this.emitEvent('ip', ip); | ||
// Emit the legacy event | ||
if (!(ip && ip.type)) { | ||
return; | ||
} | ||
if (isIP) { | ||
legacy = this.ipToLegacy(ip); | ||
event = legacy.event; | ||
payload = legacy.payload; | ||
} | ||
if (event === 'connect') { | ||
this.connected = true; | ||
} | ||
if (event === 'disconnect') { | ||
this.connected = false; | ||
} | ||
return this.emitEvent(event, payload); | ||
} | ||
}]); | ||
}; | ||
return InternalSocket; | ||
}(EventEmitter); | ||
exports.InternalSocket = InternalSocket; | ||
exports.createSocket = function() { | ||
return new InternalSocket; | ||
}; | ||
exports.createSocket = function () { | ||
return new InternalSocket(); | ||
}; |
152
lib/IP.js
@@ -0,7 +1,16 @@ | ||
"use strict"; | ||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } | ||
// NoFlo - Flow-Based Programming for JavaScript | ||
// (c) 2016-2017 Flowhub UG | ||
// NoFlo may be freely distributed under the MIT license | ||
// ## Information Packets | ||
// | ||
// IP objects are the way information is transmitted between | ||
@@ -12,6 +21,6 @@ // components running in a NoFlo network. IP objects contain | ||
// (`openBracket`, `closeBracket`). | ||
// | ||
// The component currently holding an IP object is identified | ||
// with the `owner` key. | ||
// | ||
// By default, IP objects may be sent to multiple components. | ||
@@ -21,48 +30,71 @@ // If they're set to be clonable, each component will receive | ||
// IP object working with data that is safe to clone. | ||
// | ||
// It is also possible to carry metadata with an IP object. | ||
// For example, the `datatype` and `schema` of the sending | ||
// port is transmitted with the IP object. | ||
var IP; | ||
module.exports = IP = (function() { | ||
class IP { | ||
// Valid IP types: | ||
// - 'data' | ||
// - 'openBracket' | ||
// - 'closeBracket' | ||
module.exports = /*#__PURE__*/function () { | ||
_createClass(IP, null, [{ | ||
key: "isIP", | ||
// Detects if an arbitrary value is an IP | ||
static isIP(obj) { | ||
return obj && typeof obj === 'object' && obj._isIP === true; | ||
} | ||
value: function isIP(obj) { | ||
return obj && _typeof(obj) === 'object' && obj.isIP === true; | ||
} // Creates as new IP object | ||
// Valid types: 'data', 'openBracket', 'closeBracket' | ||
// Creates as new IP object | ||
// Valid types: 'data', 'openBracket', 'closeBracket' | ||
constructor(type = 'data', data = null, options = {}) { | ||
var key, val; | ||
this.type = type; | ||
this.data = data; | ||
this._isIP = true; | ||
this.scope = null; // sync scope id | ||
this.owner = null; // packet owner process | ||
this.clonable = false; // cloning safety flag | ||
this.index = null; // addressable port index | ||
this.schema = null; | ||
this.datatype = 'all'; | ||
for (key in options) { | ||
val = options[key]; | ||
this[key] = val; | ||
} | ||
}]); | ||
function IP(type) { | ||
var _this = this; | ||
var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; | ||
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
_classCallCheck(this, IP); | ||
this.type = type || 'data'; | ||
this.data = data; | ||
this.isIP = true; | ||
this.scope = null; // sync scope id | ||
this.owner = null; // packet owner process | ||
this.clonable = false; // cloning safety flag | ||
this.index = null; // addressable port index | ||
this.schema = null; | ||
this.datatype = 'all'; | ||
if (_typeof(options) === 'object') { | ||
Object.keys(options).forEach(function (key) { | ||
_this[key] = options[key]; | ||
}); | ||
} | ||
// Creates a new IP copying its contents by value not reference | ||
clone() { | ||
var ip, key, ref, val; | ||
ip = new IP(this.type); | ||
ref = this; | ||
for (key in ref) { | ||
val = ref[key]; | ||
if (['owner'].indexOf(key) !== -1) { | ||
continue; | ||
return this; | ||
} // Creates a new IP copying its contents by value not reference | ||
_createClass(IP, [{ | ||
key: "clone", | ||
value: function clone() { | ||
var _this2 = this; | ||
var ip = new IP(this.type); | ||
Object.keys(this).forEach(function (key) { | ||
var val = _this2[key]; | ||
if (key === 'owner') { | ||
return; | ||
} | ||
if (val === null) { | ||
continue; | ||
return; | ||
} | ||
if (typeof val === 'object') { | ||
if (_typeof(val) === 'object') { | ||
ip[key] = JSON.parse(JSON.stringify(val)); | ||
@@ -72,32 +104,26 @@ } else { | ||
} | ||
} | ||
}); | ||
return ip; | ||
} | ||
} // Moves an IP to a different owner | ||
// Moves an IP to a different owner | ||
move(owner) { | ||
}, { | ||
key: "move", | ||
value: function move(owner) { | ||
// no-op | ||
this.owner = owner; | ||
} | ||
return this; | ||
} // Frees IP contents | ||
// no-op | ||
}, { | ||
key: "drop", | ||
value: function drop() { | ||
var _this3 = this; | ||
// Frees IP contents | ||
drop() { | ||
var key, ref, results, val; | ||
ref = this; | ||
results = []; | ||
for (key in ref) { | ||
val = ref[key]; | ||
results.push(delete this[key]); | ||
} | ||
return results; | ||
Object.keys(this).forEach(function (key) { | ||
delete _this3[key]; | ||
}); | ||
} | ||
}]); | ||
}; | ||
// Valid IP types | ||
IP.types = ['data', 'openBracket', 'closeBracket']; | ||
return IP; | ||
}).call(this); | ||
}(); |
@@ -1,16 +0,24 @@ | ||
var CoffeeScript, dynamicLoader, fbpGraph, fs, manifest, manifestLoader, path, registerCustomLoaders, registerModules, registerSubgraph, utils; | ||
"use strict"; | ||
path = require('path'); | ||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
fs = require('fs'); | ||
/* eslint-disable | ||
global-require, | ||
import/no-dynamic-require, | ||
no-underscore-dangle, | ||
prefer-destructuring, | ||
*/ | ||
var path = require('path'); | ||
manifest = require('fbp-manifest'); | ||
var fs = require('fs'); | ||
utils = require('../Utils'); | ||
var manifest = require('fbp-manifest'); | ||
fbpGraph = require('fbp-graph'); | ||
var fbpGraph = require('fbp-graph'); // We allow components to be un-compiled CoffeeScript | ||
// We allow components to be un-compiled CoffeeScript | ||
CoffeeScript = require('coffeescript'); | ||
var CoffeeScript = require('coffeescript'); | ||
var utils = require('../Utils'); | ||
if (typeof CoffeeScript.register !== 'undefined') { | ||
@@ -20,85 +28,119 @@ CoffeeScript.register(); | ||
registerCustomLoaders = function(loader, componentLoaders, callback) { | ||
var customLoader; | ||
function registerCustomLoaders(loader, componentLoaders, callback) { | ||
if (!componentLoaders.length) { | ||
return callback(null); | ||
callback(null); | ||
return; | ||
} | ||
customLoader = require(componentLoaders.shift()); | ||
return loader.registerLoader(customLoader, function(err) { | ||
var customLoader = require(componentLoaders.shift()); | ||
loader.registerLoader(customLoader, function (err) { | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
return registerCustomLoaders(loader, componentLoaders, callback); | ||
registerCustomLoaders(loader, componentLoaders, callback); | ||
}); | ||
}; | ||
} | ||
registerModules = function(loader, modules, callback) { | ||
var c, compatible, componentLoaders, i, j, len, len1, loaderPath, m, ref, ref1; | ||
compatible = modules.filter(function(m) { | ||
var ref; | ||
return (ref = m.runtime) === 'noflo' || ref === 'noflo-nodejs'; | ||
function registerModules(loader, modules, callback) { | ||
var compatible = modules.filter(function (m) { | ||
return ['noflo', 'noflo-nodejs'].includes(m.runtime); | ||
}); | ||
componentLoaders = []; | ||
for (i = 0, len = compatible.length; i < len; i++) { | ||
m = compatible[i]; | ||
var componentLoaders = []; | ||
compatible.forEach(function (m) { | ||
if (m.icon) { | ||
loader.setLibraryIcon(m.name, m.icon); | ||
} | ||
if ((ref = m.noflo) != null ? ref.loader : void 0) { | ||
loaderPath = path.resolve(loader.baseDir, m.base, m.noflo.loader); | ||
if (m.noflo != null ? m.noflo.loader : undefined) { | ||
var loaderPath = path.resolve(loader.baseDir, m.base, m.noflo.loader); | ||
componentLoaders.push(loaderPath); | ||
} | ||
ref1 = m.components; | ||
for (j = 0, len1 = ref1.length; j < len1; j++) { | ||
c = ref1[j]; | ||
m.components.forEach(function (c) { | ||
loader.registerComponent(m.name, c.name, path.resolve(loader.baseDir, c.path)); | ||
} | ||
}); | ||
}); | ||
registerCustomLoaders(loader, componentLoaders, callback); | ||
} | ||
var dynamicLoader = { | ||
listComponents: function listComponents(loader, manifestOptions, callback) { | ||
var opts = manifestOptions; | ||
opts.discover = true; | ||
manifest.list.list(loader.baseDir, opts, function (err, modules) { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
registerModules(loader, modules, function (err2) { | ||
if (err2) { | ||
callback(err2); | ||
return; | ||
} | ||
callback(null, modules); | ||
}); | ||
}); | ||
} | ||
return registerCustomLoaders(loader, componentLoaders, callback); | ||
}; | ||
manifestLoader = { | ||
writeCache: function(loader, options, manifest, callback) { | ||
var filePath; | ||
filePath = path.resolve(loader.baseDir, options.manifest); | ||
return fs.writeFile(filePath, JSON.stringify(manifest, null, 2), { | ||
var manifestLoader = { | ||
writeCache: function writeCache(loader, options, manifestContents, callback) { | ||
var filePath = path.resolve(loader.baseDir, options.manifest); | ||
fs.writeFile(filePath, JSON.stringify(manifestContents, null, 2), { | ||
encoding: 'utf-8' | ||
}, callback); | ||
}, | ||
readCache: function(loader, options, callback) { | ||
options.discover = false; | ||
return manifest.load.load(loader.baseDir, options, callback); | ||
readCache: function readCache(loader, options, callback) { | ||
var opts = options; | ||
opts.discover = false; | ||
manifest.load.load(loader.baseDir, opts, callback); | ||
}, | ||
prepareManifestOptions: function(loader) { | ||
var options; | ||
if (!loader.options) { | ||
loader.options = {}; | ||
prepareManifestOptions: function prepareManifestOptions(loader) { | ||
var l = loader; | ||
if (!l.options) { | ||
l.options = {}; | ||
} | ||
options = {}; | ||
options.runtimes = loader.options.runtimes || []; | ||
var options = {}; | ||
options.runtimes = l.options.runtimes || []; | ||
if (options.runtimes.indexOf('noflo') === -1) { | ||
options.runtimes.push('noflo'); | ||
} | ||
options.recursive = typeof loader.options.recursive === 'undefined' ? true : loader.options.recursive; | ||
options.manifest = loader.options.manifest || 'fbp.json'; | ||
options.recursive = typeof l.options.recursive === 'undefined' ? true : l.options.recursive; | ||
options.manifest = l.options.manifest || 'fbp.json'; | ||
return options; | ||
}, | ||
listComponents: function(loader, manifestOptions, callback) { | ||
return this.readCache(loader, manifestOptions, (err, manifest) => { | ||
listComponents: function listComponents(loader, manifestOptions, callback) { | ||
var _this = this; | ||
this.readCache(loader, manifestOptions, function (err, manifestContents) { | ||
if (err) { | ||
if (!loader.options.discover) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
dynamicLoader.listComponents(loader, manifestOptions, (err, modules) => { | ||
if (err) { | ||
return callback(err); | ||
dynamicLoader.listComponents(loader, manifestOptions, function (err2, modules) { | ||
if (err2) { | ||
callback(err2); | ||
return; | ||
} | ||
return this.writeCache(loader, manifestOptions, { | ||
_this.writeCache(loader, manifestOptions, { | ||
version: 1, | ||
modules: modules | ||
}, function(err) { | ||
if (err) { | ||
return callback(err); | ||
}, function (err3) { | ||
if (err3) { | ||
callback(err3); | ||
return; | ||
} | ||
return callback(null, modules); | ||
callback(null, modules); | ||
}); | ||
@@ -108,24 +150,10 @@ }); | ||
} | ||
return registerModules(loader, manifest.modules, function(err) { | ||
if (err) { | ||
return callback(err); | ||
registerModules(loader, manifestContents.modules, function (err2) { | ||
if (err2) { | ||
callback(err2); | ||
return; | ||
} | ||
return callback(null, manifest.modules); | ||
}); | ||
}); | ||
} | ||
}; | ||
dynamicLoader = { | ||
listComponents: function(loader, manifestOptions, callback) { | ||
manifestOptions.discover = true; | ||
return manifest.list.list(loader.baseDir, manifestOptions, (err, modules) => { | ||
if (err) { | ||
return callback(err); | ||
} | ||
return registerModules(loader, modules, function(err) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
return callback(null, modules); | ||
callback(null, manifestContents.modules); | ||
}); | ||
@@ -136,50 +164,51 @@ }); | ||
registerSubgraph = function(loader) { | ||
var graphPath; | ||
function registerSubgraph(loader) { | ||
// Inject subgraph component | ||
if (path.extname(__filename) === '.js') { | ||
graphPath = path.resolve(__dirname, '../../components/Graph.js'); | ||
} else { | ||
graphPath = path.resolve(__dirname, '../../components/Graph.coffee'); | ||
} | ||
return loader.registerComponent(null, 'Graph', graphPath); | ||
}; | ||
var graphPath = path.resolve(__dirname, '../../components/Graph.js'); | ||
loader.registerComponent(null, 'Graph', graphPath); | ||
} | ||
exports.register = function(loader, callback) { | ||
var manifestOptions, ref; | ||
manifestOptions = manifestLoader.prepareManifestOptions(loader); | ||
if ((ref = loader.options) != null ? ref.cache : void 0) { | ||
manifestLoader.listComponents(loader, manifestOptions, function(err, modules) { | ||
exports.register = function register(loader, callback) { | ||
var manifestOptions = manifestLoader.prepareManifestOptions(loader); | ||
if (loader.options != null ? loader.options.cache : undefined) { | ||
manifestLoader.listComponents(loader, manifestOptions, function (err, modules) { | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
registerSubgraph(loader); | ||
return callback(null, modules); | ||
callback(null, modules); | ||
}); | ||
return; | ||
} | ||
return dynamicLoader.listComponents(loader, manifestOptions, function(err, modules) { | ||
dynamicLoader.listComponents(loader, manifestOptions, function (err, modules) { | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
registerSubgraph(loader); | ||
return callback(null, modules); | ||
callback(null, modules); | ||
}); | ||
}; | ||
exports.dynamicLoad = function(name, cPath, metadata, callback) { | ||
var e, implementation, instance; | ||
exports.dynamicLoad = function dynamicLoad(name, cPath, metadata, callback) { | ||
var implementation; | ||
var instance; | ||
try { | ||
implementation = require(cPath); | ||
} catch (error) { | ||
e = error; | ||
callback(e); | ||
} catch (err) { | ||
callback(err); | ||
return; | ||
} | ||
if (typeof implementation.getComponent === 'function') { | ||
try { | ||
instance = implementation.getComponent(metadata); | ||
} catch (error) { | ||
e = error; | ||
callback(e); | ||
} catch (err) { | ||
callback(err); | ||
return; | ||
@@ -190,65 +219,85 @@ } | ||
instance = implementation(metadata); | ||
} catch (error) { | ||
e = error; | ||
callback(e); | ||
} catch (err) { | ||
callback(err); | ||
return; | ||
} | ||
} else { | ||
callback(new Error(`Unable to instantiate ${cPath}`)); | ||
callback(new Error("Unable to instantiate ".concat(cPath))); | ||
return; | ||
} | ||
if (typeof name === 'string') { | ||
instance.componentName = name; | ||
} | ||
return callback(null, instance); | ||
callback(null, instance); | ||
}; | ||
exports.setSource = function(loader, packageId, name, source, language, callback) { | ||
var Module, e, implementation, moduleImpl, modulePath; | ||
Module = require('module'); | ||
exports.setSource = function setSource(loader, packageId, name, source, language, callback) { | ||
var Module = require('module'); | ||
var src = source; | ||
if (language === 'coffeescript') { | ||
try { | ||
source = CoffeeScript.compile(source, { | ||
src = CoffeeScript.compile(src, { | ||
bare: true | ||
}); | ||
} catch (error) { | ||
e = error; | ||
return callback(e); | ||
} catch (err) { | ||
callback(err); | ||
return; | ||
} | ||
} | ||
var implementation; | ||
try { | ||
// Use the Node.js module API to evaluate in the correct directory context | ||
modulePath = path.resolve(loader.baseDir, `./components/${name}.js`); | ||
moduleImpl = new Module(modulePath, module); | ||
var modulePath = path.resolve(loader.baseDir, "./components/".concat(name, ".js")); | ||
var moduleImpl = new Module(modulePath, module); | ||
moduleImpl.paths = Module._nodeModulePaths(path.dirname(modulePath)); | ||
moduleImpl.filename = modulePath; | ||
moduleImpl._compile(source, modulePath); | ||
moduleImpl._compile(src, modulePath); | ||
implementation = moduleImpl.exports; | ||
} catch (error) { | ||
e = error; | ||
return callback(e); | ||
} catch (err) { | ||
callback(err); | ||
return; | ||
} | ||
if (!(typeof implementation === 'function' || typeof implementation.getComponent === 'function')) { | ||
return callback(new Error('Provided source failed to create a runnable component')); | ||
if (typeof implementation !== 'function' && typeof implementation.getComponent !== 'function') { | ||
callback(new Error('Provided source failed to create a runnable component')); | ||
return; | ||
} | ||
return loader.registerComponent(packageId, name, implementation, callback); | ||
loader.registerComponent(packageId, name, implementation, callback); | ||
}; | ||
exports.getSource = function(loader, name, callback) { | ||
var component, componentName, nameParts; | ||
component = loader.components[name]; | ||
exports.getSource = function getSource(loader, name, callback) { | ||
var componentName = name; | ||
var component = loader.components[name]; | ||
if (!component) { | ||
// Try an alias | ||
for (componentName in loader.components) { | ||
if (componentName.split('/')[1] === name) { | ||
component = loader.components[componentName]; | ||
name = componentName; | ||
// Try an alias | ||
var keys = Object.keys(loader.components); | ||
for (var i = 0; i < keys.length; i += 1) { | ||
var key = keys[i]; | ||
if (key.split('/')[1] === name) { | ||
component = loader.components[key]; | ||
componentName = key; | ||
break; | ||
} | ||
} | ||
if (!component) { | ||
return callback(new Error(`Component ${name} not installed`)); | ||
callback(new Error("Component ".concat(componentName, " not installed"))); | ||
return; | ||
} | ||
} | ||
nameParts = name.split('/'); | ||
var nameParts = componentName.split('/'); | ||
if (nameParts.length === 1) { | ||
@@ -258,4 +307,5 @@ nameParts[1] = nameParts[0]; | ||
} | ||
if (loader.isGraph(component)) { | ||
if (typeof component === 'object') { | ||
if (_typeof(component) === 'object') { | ||
if (typeof component.toJSON === 'function') { | ||
@@ -270,12 +320,19 @@ callback(null, { | ||
} | ||
return callback(new Error(`Can't provide source for ${name}. Not a file`)); | ||
callback(new Error("Can't provide source for ".concat(componentName, ". Not a file"))); | ||
return; | ||
} | ||
fbpGraph.graph.loadFile(component, function(err, graph) { | ||
fbpGraph.graph.loadFile(component, function (err, graph) { | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
if (!graph) { | ||
return callback(new Error('Unable to load graph')); | ||
callback(new Error('Unable to load graph')); | ||
return; | ||
} | ||
return callback(null, { | ||
callback(null, { | ||
name: nameParts[1], | ||
@@ -289,10 +346,15 @@ library: nameParts[0], | ||
} | ||
if (typeof component !== 'string') { | ||
return callback(new Error(`Can't provide source for ${name}. Not a file`)); | ||
callback(new Error("Can't provide source for ".concat(componentName, ". Not a file"))); | ||
return; | ||
} | ||
return fs.readFile(component, 'utf-8', function(err, code) { | ||
fs.readFile(component, 'utf-8', function (err, code) { | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
return callback(null, { | ||
callback(null, { | ||
name: nameParts[1], | ||
@@ -304,2 +366,2 @@ library: nameParts[0], | ||
}); | ||
}; | ||
}; |
@@ -1,4 +0,8 @@ | ||
var isBrowser; | ||
"use strict"; | ||
({isBrowser} = require('../Platform')); | ||
/* eslint-disable | ||
global-require, | ||
*/ | ||
var _require = require('../Platform'), | ||
isBrowser = _require.isBrowser; | ||
@@ -9,2 +13,2 @@ if (isBrowser()) { | ||
module.exports = require('./NodeJs'); | ||
} | ||
} |
1097
lib/Network.js
@@ -1,1025 +0,216 @@ | ||
// NoFlo - Flow-Based Programming for JavaScript | ||
// (c) 2013-2017 Flowhub UG | ||
// (c) 2011-2012 Henri Bergius, Nemein | ||
// NoFlo may be freely distributed under the MIT license | ||
var EventEmitter, IP, Network, componentLoader, graph, internalSocket, platform, utils; | ||
"use strict"; | ||
internalSocket = require("./InternalSocket"); | ||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
graph = require("fbp-graph"); | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
({EventEmitter} = require('events')); | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
platform = require('./Platform'); | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } | ||
componentLoader = require('./ComponentLoader'); | ||
function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); } | ||
utils = require('./Utils'); | ||
function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } | ||
IP = require('./IP'); | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } | ||
Network = (function() { | ||
// ## The NoFlo network coordinator | ||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } | ||
// NoFlo networks consist of processes connected to each other | ||
// via sockets attached from outports to inports. | ||
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } | ||
// The role of the network coordinator is to take a graph and | ||
// instantiate all the necessary processes from the designated | ||
// components, attach sockets between them, and handle the sending | ||
// of Initial Information Packets. | ||
class Network extends EventEmitter { | ||
// All NoFlo networks are instantiated with a graph. Upon instantiation | ||
// they will load all the needed components, instantiate them, and | ||
// set up the defined connections and IIPs. | ||
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } | ||
// The network will also listen to graph changes and modify itself | ||
// accordingly, including removing connections, adding new nodes, | ||
// and sending new IIPs. | ||
constructor(graph, options = {}) { | ||
super(); | ||
this.options = options; | ||
this.processes = {}; | ||
this.connections = []; | ||
this.initials = []; | ||
this.nextInitials = []; | ||
this.defaults = []; | ||
this.graph = graph; | ||
this.started = false; | ||
this.stopped = true; | ||
this.debug = true; | ||
this.eventBuffer = []; | ||
// On Node.js we default the baseDir for component loading to | ||
// the current working directory | ||
if (!platform.isBrowser()) { | ||
this.baseDir = graph.baseDir || process.cwd(); | ||
} else { | ||
// On browser we default the baseDir to the Component loading | ||
// root | ||
this.baseDir = graph.baseDir || '/'; | ||
} | ||
// As most NoFlo networks are long-running processes, the | ||
// network coordinator marks down the start-up time. This | ||
// way we can calculate the uptime of the network. | ||
this.startupDate = null; | ||
// Initialize a Component Loader for the network | ||
if (graph.componentLoader) { | ||
this.loader = graph.componentLoader; | ||
} else { | ||
this.loader = new componentLoader.ComponentLoader(this.baseDir, this.options); | ||
} | ||
} | ||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } | ||
// The uptime of the network is the current time minus the start-up | ||
// time, in seconds. | ||
uptime() { | ||
if (!this.startupDate) { | ||
return 0; | ||
} | ||
return new Date() - this.startupDate; | ||
} | ||
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } | ||
getActiveProcesses() { | ||
var active, name, process, ref; | ||
active = []; | ||
if (!this.started) { | ||
return active; | ||
} | ||
ref = this.processes; | ||
for (name in ref) { | ||
process = ref[name]; | ||
if (process.component.load > 0) { | ||
// Modern component with load | ||
active.push(name); | ||
} | ||
if (process.component.__openConnections > 0) { | ||
// Legacy component | ||
active.push(name); | ||
} | ||
} | ||
return active; | ||
} | ||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } | ||
bufferedEmit(event, payload) { | ||
var ev, i, len, ref; | ||
// Errors get emitted immediately, like does network end | ||
if (event === 'icon' || event === 'error' || event === 'process-error' || event === 'end') { | ||
this.emit(event, payload); | ||
return; | ||
} | ||
if (!this.isStarted() && event !== 'end') { | ||
this.eventBuffer.push({ | ||
type: event, | ||
payload: payload | ||
}); | ||
return; | ||
} | ||
this.emit(event, payload); | ||
if (event === 'start') { | ||
ref = this.eventBuffer; | ||
// Once network has started we can send the IP-related events | ||
for (i = 0, len = ref.length; i < len; i++) { | ||
ev = ref[i]; | ||
this.emit(ev.type, ev.payload); | ||
} | ||
this.eventBuffer = []; | ||
} | ||
if (event === 'ip') { | ||
// Emit also the legacy events from IP | ||
switch (payload.type) { | ||
case 'openBracket': | ||
this.bufferedEmit('begingroup', payload); | ||
break; | ||
case 'closeBracket': | ||
this.bufferedEmit('endgroup', payload); | ||
break; | ||
case 'data': | ||
this.bufferedEmit('data', payload); | ||
} | ||
} | ||
} | ||
// NoFlo - Flow-Based Programming for JavaScript | ||
// (c) 2013-2018 Flowhub UG | ||
// (c) 2011-2012 Henri Bergius, Nemein | ||
// NoFlo may be freely distributed under the MIT license | ||
var BaseNetwork = require('./BaseNetwork'); | ||
/* eslint-disable | ||
no-param-reassign, | ||
*/ | ||
// ## The NoFlo network coordinator | ||
// | ||
// NoFlo networks consist of processes connected to each other | ||
// via sockets attached from outports to inports. | ||
// | ||
// The role of the network coordinator is to take a graph and | ||
// instantiate all the necessary processes from the designated | ||
// components, attach sockets between them, and handle the sending | ||
// of Initial Information Packets. | ||
// ## Loading components | ||
// Components can be passed to the NoFlo network in two ways: | ||
var Network = /*#__PURE__*/function (_BaseNetwork) { | ||
_inherits(Network, _BaseNetwork); | ||
// * As direct, instantiated JavaScript objects | ||
// * As filenames | ||
load(component, metadata, callback) { | ||
return this.loader.load(component, callback, metadata); | ||
} | ||
var _super = _createSuper(Network); | ||
// ## Add a process to the network | ||
// All NoFlo networks are instantiated with a graph. Upon instantiation | ||
// they will load all the needed components, instantiate them, and | ||
// set up the defined connections and IIPs. | ||
function Network(graph) { | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
// Processes can be added to a network at either start-up time | ||
// or later. The processes are added with a node definition object | ||
// that includes the following properties: | ||
_classCallCheck(this, Network); | ||
// * `id`: Identifier of the process in the network. Typically a string | ||
// * `component`: Filename or path of a NoFlo component, or a component instance object | ||
addNode(node, callback) { | ||
var process; | ||
// Processes are treated as singletons by their identifier. If | ||
// we already have a process with the given ID, return that. | ||
if (this.processes[node.id]) { | ||
callback(null, this.processes[node.id]); | ||
return; | ||
return _super.call(this, graph, options); | ||
} // Add a process to the network. The node will also be registered | ||
// with the current graph. | ||
_createClass(Network, [{ | ||
key: "addNode", | ||
value: function addNode(node, options, callback) { | ||
var _this = this; | ||
if (typeof options === 'function') { | ||
callback = options; | ||
options = {}; | ||
} | ||
process = { | ||
id: node.id | ||
}; | ||
// No component defined, just register the process but don't start. | ||
if (!node.component) { | ||
this.processes[process.id] = process; | ||
callback(null, process); | ||
return; | ||
} | ||
// Load the component for the process. | ||
return this.load(node.component, node.metadata, (err, instance) => { | ||
var inPorts, name, outPorts, port; | ||
if (err) { | ||
return callback(err); | ||
} | ||
instance.nodeId = node.id; | ||
process.component = instance; | ||
process.componentName = node.component; | ||
// Inform the ports of the node name | ||
inPorts = process.component.inPorts.ports; | ||
outPorts = process.component.outPorts.ports; | ||
for (name in inPorts) { | ||
port = inPorts[name]; | ||
port.node = node.id; | ||
port.nodeInstance = instance; | ||
port.name = name; | ||
} | ||
for (name in outPorts) { | ||
port = outPorts[name]; | ||
port.node = node.id; | ||
port.nodeInstance = instance; | ||
port.name = name; | ||
} | ||
if (instance.isSubgraph()) { | ||
this.subscribeSubgraph(process); | ||
} | ||
this.subscribeNode(process); | ||
// Store and return the process instance | ||
this.processes[process.id] = process; | ||
return callback(null, process); | ||
}); | ||
} | ||
removeNode(node, callback) { | ||
var process; | ||
process = this.getNode(node.id); | ||
if (!process) { | ||
return callback(new Error(`Node ${node.id} not found`)); | ||
} | ||
return process.component.shutdown((err) => { | ||
_get(_getPrototypeOf(Network.prototype), "addNode", this).call(this, node, options, function (err, process) { | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
delete this.processes[node.id]; | ||
return callback(null); | ||
}); | ||
} | ||
renameNode(oldId, newId, callback) { | ||
var inPorts, name, outPorts, port, process; | ||
process = this.getNode(oldId); | ||
if (!process) { | ||
return callback(new Error(`Process ${oldId} not found`)); | ||
} | ||
// Inform the process of its ID | ||
process.id = newId; | ||
// Inform the ports of the node name | ||
inPorts = process.component.inPorts.ports; | ||
outPorts = process.component.outPorts.ports; | ||
for (name in inPorts) { | ||
port = inPorts[name]; | ||
if (!port) { | ||
continue; | ||
if (!options.initial) { | ||
_this.graph.addNode(node.id, node.component, node.metadata); | ||
} | ||
port.node = newId; | ||
} | ||
for (name in outPorts) { | ||
port = outPorts[name]; | ||
if (!port) { | ||
continue; | ||
} | ||
port.node = newId; | ||
} | ||
this.processes[newId] = process; | ||
delete this.processes[oldId]; | ||
return callback(null); | ||
} | ||
// Get process by its ID. | ||
getNode(id) { | ||
return this.processes[id]; | ||
} | ||
connect(done = function() {}) { | ||
var callStack, edges, initializers, nodes, serialize, setDefaults, subscribeGraph; | ||
// Wrap the future which will be called when done in a function and return | ||
// it | ||
callStack = 0; | ||
serialize = (next, add) => { | ||
return (type) => { | ||
// Add either a Node, an Initial, or an Edge and move on to the next one | ||
// when done | ||
return this[`add${type}`](add, function(err) { | ||
if (err) { | ||
return done(err); | ||
} | ||
callStack++; | ||
if (callStack % 100 === 0) { | ||
setTimeout(function() { | ||
return next(type); | ||
}, 0); | ||
return; | ||
} | ||
return next(type); | ||
}); | ||
}; | ||
}; | ||
// Subscribe to graph changes when everything else is done | ||
subscribeGraph = () => { | ||
this.subscribeGraph(); | ||
return done(); | ||
}; | ||
// Serialize default socket creation then call callback when done | ||
setDefaults = utils.reduceRight(this.graph.nodes, serialize, subscribeGraph); | ||
// Serialize initializers then call defaults. | ||
initializers = utils.reduceRight(this.graph.initializers, serialize, function() { | ||
return setDefaults("Defaults"); | ||
callback(null, process); | ||
}); | ||
// Serialize edge creators then call the initializers. | ||
edges = utils.reduceRight(this.graph.edges, serialize, function() { | ||
return initializers("Initial"); | ||
}); | ||
// Serialize node creators then call the edge creators | ||
nodes = utils.reduceRight(this.graph.nodes, serialize, function() { | ||
return edges("Edge"); | ||
}); | ||
// Start with node creators | ||
return nodes("Node"); | ||
} | ||
} // Remove a process from the network. The node will also be removed | ||
// from the current graph. | ||
connectPort(socket, process, port, index, inbound, callback) { | ||
if (inbound) { | ||
socket.to = { | ||
process: process, | ||
port: port, | ||
index: index | ||
}; | ||
if (!(process.component.inPorts && process.component.inPorts[port])) { | ||
callback(new Error(`No inport '${port}' defined in process ${process.id} (${socket.getId()})`)); | ||
}, { | ||
key: "removeNode", | ||
value: function removeNode(node, callback) { | ||
var _this2 = this; | ||
_get(_getPrototypeOf(Network.prototype), "removeNode", this).call(this, node, function (err) { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
if (process.component.inPorts[port].isAddressable()) { | ||
process.component.inPorts[port].attach(socket, index); | ||
callback(); | ||
return; | ||
} | ||
process.component.inPorts[port].attach(socket); | ||
_this2.graph.removeNode(node.id); | ||
callback(); | ||
return; | ||
} | ||
socket.from = { | ||
process: process, | ||
port: port, | ||
index: index | ||
}; | ||
if (!(process.component.outPorts && process.component.outPorts[port])) { | ||
callback(new Error(`No outport '${port}' defined in process ${process.id} (${socket.getId()})`)); | ||
return; | ||
} | ||
if (process.component.outPorts[port].isAddressable()) { | ||
process.component.outPorts[port].attach(socket, index); | ||
callback(); | ||
return; | ||
} | ||
process.component.outPorts[port].attach(socket); | ||
callback(); | ||
} | ||
}); | ||
} // Rename a process in the network. Renaming a process also modifies | ||
// the current graph. | ||
subscribeGraph() { | ||
var graphOps, processOps, processing, registerOp; | ||
// A NoFlo graph may change after network initialization. | ||
// For this, the network subscribes to the change events from | ||
// the graph. | ||
}, { | ||
key: "renameNode", | ||
value: function renameNode(oldId, newId, callback) { | ||
var _this3 = this; | ||
// In graph we talk about nodes and edges. Nodes correspond | ||
// to NoFlo processes, and edges to connections between them. | ||
graphOps = []; | ||
processing = false; | ||
registerOp = function(op, details) { | ||
return graphOps.push({ | ||
op: op, | ||
details: details | ||
}); | ||
}; | ||
processOps = (err) => { | ||
var cb, op; | ||
_get(_getPrototypeOf(Network.prototype), "renameNode", this).call(this, oldId, newId, function (err) { | ||
if (err) { | ||
if (this.listeners('process-error').length === 0) { | ||
throw err; | ||
} | ||
this.bufferedEmit('process-error', err); | ||
} | ||
if (!graphOps.length) { | ||
processing = false; | ||
callback(err); | ||
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.graph.on('addNode', function(node) { | ||
registerOp('addNode', node); | ||
if (!processing) { | ||
return processOps(); | ||
} | ||
}); | ||
this.graph.on('removeNode', function(node) { | ||
registerOp('removeNode', node); | ||
if (!processing) { | ||
return processOps(); | ||
} | ||
}); | ||
this.graph.on('renameNode', function(oldId, newId) { | ||
registerOp('renameNode', { | ||
from: oldId, | ||
to: newId | ||
}); | ||
if (!processing) { | ||
return processOps(); | ||
} | ||
}); | ||
this.graph.on('addEdge', function(edge) { | ||
registerOp('addEdge', edge); | ||
if (!processing) { | ||
return processOps(); | ||
} | ||
}); | ||
this.graph.on('removeEdge', function(edge) { | ||
registerOp('removeEdge', edge); | ||
if (!processing) { | ||
return processOps(); | ||
} | ||
}); | ||
this.graph.on('addInitial', function(iip) { | ||
registerOp('addInitial', iip); | ||
if (!processing) { | ||
return processOps(); | ||
} | ||
}); | ||
return this.graph.on('removeInitial', function(iip) { | ||
registerOp('removeInitial', iip); | ||
if (!processing) { | ||
return processOps(); | ||
} | ||
}); | ||
} | ||
subscribeSubgraph(node) { | ||
var emitSub; | ||
if (!node.component.isReady()) { | ||
node.component.once('ready', () => { | ||
return this.subscribeSubgraph(node); | ||
}); | ||
return; | ||
} | ||
if (!node.component.network) { | ||
return; | ||
} | ||
node.component.network.setDebug(this.debug); | ||
emitSub = (type, data) => { | ||
if (type === 'process-error' && this.listeners('process-error').length === 0) { | ||
if (data.id && data.metadata && data.error) { | ||
throw data.error; | ||
} | ||
throw data; | ||
} | ||
if (!data) { | ||
data = {}; | ||
} | ||
if (data.subgraph) { | ||
if (!data.subgraph.unshift) { | ||
data.subgraph = [data.subgraph]; | ||
} | ||
data.subgraph.unshift(node.id); | ||
} else { | ||
data.subgraph = [node.id]; | ||
} | ||
return this.bufferedEmit(type, data); | ||
}; | ||
node.component.network.on('ip', function(data) { | ||
return emitSub('ip', data); | ||
}); | ||
return node.component.network.on('process-error', function(data) { | ||
return emitSub('process-error', data); | ||
}); | ||
} | ||
_this3.graph.renameNode(oldId, newId); | ||
// Subscribe to events from all connected sockets and re-emit them | ||
subscribeSocket(socket, source) { | ||
var ref; | ||
socket.on('ip', (ip) => { | ||
return this.bufferedEmit('ip', { | ||
id: socket.getId(), | ||
type: ip.type, | ||
socket: socket, | ||
data: ip.data, | ||
metadata: socket.metadata | ||
}); | ||
callback(); | ||
}); | ||
socket.on('error', (event) => { | ||
if (this.listeners('process-error').length === 0) { | ||
if (event.id && event.metadata && event.error) { | ||
throw event.error; | ||
} | ||
throw event; | ||
} | ||
return this.bufferedEmit('process-error', event); | ||
}); | ||
if (!(source != null ? (ref = source.component) != null ? ref.isLegacy() : void 0 : void 0)) { | ||
return; | ||
} | ||
// Handle activation for legacy components via connects/disconnects | ||
socket.on('connect', function() { | ||
if (!source.component.__openConnections) { | ||
source.component.__openConnections = 0; | ||
} | ||
return source.component.__openConnections++; | ||
}); | ||
return socket.on('disconnect', () => { | ||
source.component.__openConnections--; | ||
if (source.component.__openConnections < 0) { | ||
source.component.__openConnections = 0; | ||
} | ||
if (source.component.__openConnections === 0) { | ||
return this.checkIfFinished(); | ||
} | ||
}); | ||
} | ||
} // Add a connection to the network. The edge will also be registered | ||
// with the current graph. | ||
subscribeNode(node) { | ||
node.component.on('activate', (load) => { | ||
if (this.debouncedEnd) { | ||
return this.abortDebounce = true; | ||
} | ||
}); | ||
node.component.on('deactivate', (load) => { | ||
if (load > 0) { | ||
return; | ||
} | ||
return this.checkIfFinished(); | ||
}); | ||
if (!node.component.getIcon) { | ||
return; | ||
}, { | ||
key: "addEdge", | ||
value: function addEdge(edge, options, callback) { | ||
var _this4 = this; | ||
if (typeof options === 'function') { | ||
callback = options; | ||
options = {}; | ||
} | ||
return node.component.on('icon', () => { | ||
return this.bufferedEmit('icon', { | ||
id: node.id, | ||
icon: node.component.getIcon() | ||
}); | ||
}); | ||
} | ||
addEdge(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", () => { | ||
return this.addEdge(edge, callback); | ||
}); | ||
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", () => { | ||
return this.addEdge(edge, callback); | ||
}); | ||
return; | ||
} | ||
// Subscribe to events from the socket | ||
this.subscribeSocket(socket, from); | ||
return this.connectPort(socket, to, edge.to.port, edge.to.index, true, (err) => { | ||
_get(_getPrototypeOf(Network.prototype), "addEdge", this).call(this, edge, options, function (err) { | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
return this.connectPort(socket, from, edge.from.port, edge.from.index, false, (err) => { | ||
if (err) { | ||
return callback(err); | ||
} | ||
this.connections.push(socket); | ||
return callback(); | ||
}); | ||
}); | ||
} | ||
removeEdge(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 (!options.initial) { | ||
_this4.graph.addEdgeIndex(edge.from.node, edge.from.port, edge.from.index, edge.to.node, edge.to.port, edge.to.index, edge.metadata); | ||
} | ||
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); | ||
results.push(callback()); | ||
} | ||
return results; | ||
} | ||
addDefaults(node, callback) { | ||
var key, port, process, ref, socket; | ||
process = this.getNode(node.id); | ||
if (!process) { | ||
return callback(new Error(`Process ${node.id} not defined`)); | ||
} | ||
if (!process.component) { | ||
return callback(new Error(`No component defined for node ${node.id}`)); | ||
} | ||
if (!process.component.isReady()) { | ||
process.component.setMaxListeners(0); | ||
process.component.once("ready", () => { | ||
return this.addDefaults(process, callback); | ||
}); | ||
return; | ||
} | ||
ref = process.component.inPorts.ports; | ||
for (key in ref) { | ||
port = ref[key]; | ||
// Attach a socket to any defaulted inPorts as long as they aren't already attached. | ||
if (port.hasDefault() && !port.isAttached()) { | ||
socket = internalSocket.createSocket(); | ||
socket.setDebug(this.debug); | ||
// Subscribe to events from the socket | ||
this.subscribeSocket(socket); | ||
this.connectPort(socket, process, key, void 0, true, function() {}); | ||
this.connections.push(socket); | ||
this.defaults.push(socket); | ||
} | ||
} | ||
return callback(); | ||
} | ||
addInitial(initializer, callback) { | ||
var socket, to; | ||
socket = internalSocket.createSocket(initializer.metadata); | ||
socket.setDebug(this.debug); | ||
// Subscribe to events from the socket | ||
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) { | ||
return callback(new Error(`No component defined for inbound node ${initializer.to.node}`)); | ||
} | ||
if (!(to.component.isReady() || to.component.inPorts[initializer.to.port])) { | ||
to.component.setMaxListeners(0); | ||
to.component.once("ready", () => { | ||
return this.addInitial(initializer, callback); | ||
}); | ||
return; | ||
} | ||
return this.connectPort(socket, to, initializer.to.port, initializer.to.index, true, (err) => { | ||
var init; | ||
if (err) { | ||
return callback(err); | ||
} | ||
this.connections.push(socket); | ||
init = { | ||
socket: socket, | ||
data: initializer.from.data | ||
}; | ||
this.initials.push(init); | ||
this.nextInitials.push(init); | ||
if (this.isRunning()) { | ||
// Network is running now, send initials immediately | ||
this.sendInitials(); | ||
} else if (!this.isStopped()) { | ||
// Network has finished but hasn't been stopped, set | ||
// started and set | ||
this.setStarted(true); | ||
this.sendInitials(); | ||
} | ||
return callback(); | ||
callback(); | ||
}); | ||
} | ||
} // Remove a connection from the network. The edge will also be removed | ||
// from the current graph. | ||
removeInitial(initializer, callback) { | ||
var connection, i, init, 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); | ||
} | ||
} | ||
return callback(); | ||
} | ||
}, { | ||
key: "removeEdge", | ||
value: function removeEdge(edge, callback) { | ||
var _this5 = this; | ||
sendInitial(initial) { | ||
return initial.socket.post(new IP('data', initial.data, { | ||
initial: true | ||
})); | ||
} | ||
sendInitials(callback) { | ||
var send; | ||
if (!callback) { | ||
callback = function() {}; | ||
} | ||
send = () => { | ||
var i, initial, len, ref; | ||
ref = this.initials; | ||
for (i = 0, len = ref.length; i < len; i++) { | ||
initial = ref[i]; | ||
this.sendInitial(initial); | ||
_get(_getPrototypeOf(Network.prototype), "removeEdge", this).call(this, edge, function (err) { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
this.initials = []; | ||
return callback(); | ||
}; | ||
if (typeof process !== 'undefined' && process.execPath && process.execPath.indexOf('node') !== -1) { | ||
// nextTick is faster on Node.js | ||
return process.nextTick(send); | ||
} else { | ||
return setTimeout(send, 0); | ||
} | ||
} | ||
isStarted() { | ||
return this.started; | ||
} | ||
_this5.graph.removeEdge(edge.from.node, edge.from.port, edge.to.node, edge.to.port); | ||
isStopped() { | ||
return this.stopped; | ||
} | ||
callback(); | ||
}); | ||
} // Add an IIP to the network. The IIP will also be registered with the | ||
// current graph. If the network is running, the IIP will be sent immediately. | ||
isRunning() { | ||
return this.getActiveProcesses().length > 0; | ||
} | ||
}, { | ||
key: "addInitial", | ||
value: function addInitial(iip, options, callback) { | ||
var _this6 = this; | ||
startComponents(callback) { | ||
var count, id, length, onProcessStart, process, ref, results; | ||
if (!callback) { | ||
callback = function() {}; | ||
if (typeof options === 'function') { | ||
callback = options; | ||
options = {}; | ||
} | ||
// Emit start event when all processes are started | ||
count = 0; | ||
length = this.processes ? Object.keys(this.processes).length : 0; | ||
onProcessStart = function(err) { | ||
_get(_getPrototypeOf(Network.prototype), "addInitial", this).call(this, iip, options, function (err) { | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
count++; | ||
if (count === length) { | ||
return callback(); | ||
} | ||
}; | ||
if (!(this.processes && Object.keys(this.processes).length)) { | ||
// Perform any startup routines necessary for every component. | ||
return callback(); | ||
} | ||
ref = this.processes; | ||
results = []; | ||
for (id in ref) { | ||
process = ref[id]; | ||
if (process.component.isStarted()) { | ||
onProcessStart(); | ||
continue; | ||
} | ||
if (process.component.start.length === 0) { | ||
platform.deprecated('component.start method without callback is deprecated'); | ||
process.component.start(); | ||
onProcessStart(); | ||
continue; | ||
} | ||
results.push(process.component.start(onProcessStart)); | ||
} | ||
return results; | ||
} | ||
sendDefaults(callback) { | ||
var i, len, ref, socket; | ||
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) { | ||
// Don't send defaults if more than one socket is present on the port. | ||
// This case should only happen when a subgraph is created as a component | ||
// as its network is instantiated and its inputs are serialized before | ||
// a socket is attached from the "parent" graph. | ||
continue; | ||
if (!options.initial) { | ||
_this6.graph.addInitialIndex(iip.from.data, iip.to.node, iip.to.port, iip.to.index, iip.metadata); | ||
} | ||
socket.connect(); | ||
socket.send(); | ||
socket.disconnect(); | ||
} | ||
return callback(); | ||
} | ||
start(callback) { | ||
if (!callback) { | ||
platform.deprecated('Calling network.start() without callback is deprecated'); | ||
callback = function() {}; | ||
} | ||
if (this.debouncedEnd) { | ||
this.abortDebounce = true; | ||
} | ||
if (this.started) { | ||
this.stop((err) => { | ||
if (err) { | ||
return callback(err); | ||
} | ||
return this.start(callback); | ||
}); | ||
return; | ||
} | ||
this.initials = this.nextInitials.slice(0); | ||
this.eventBuffer = []; | ||
this.startComponents((err) => { | ||
if (err) { | ||
return callback(err); | ||
} | ||
this.sendInitials((err) => { | ||
if (err) { | ||
return callback(err); | ||
} | ||
this.sendDefaults((err) => { | ||
if (err) { | ||
return callback(err); | ||
} | ||
this.setStarted(true); | ||
callback(null); | ||
}); | ||
}); | ||
callback(); | ||
}); | ||
} | ||
} // Remove an IIP from the network. The IIP will also be removed from the | ||
// current graph. | ||
stop(callback) { | ||
var connection, count, i, id, len, length, onProcessEnd, process, ref, ref1, results; | ||
if (!callback) { | ||
platform.deprecated('Calling network.stop() without callback is deprecated'); | ||
callback = function() {}; | ||
} | ||
if (this.debouncedEnd) { | ||
this.abortDebounce = true; | ||
} | ||
if (!this.started) { | ||
this.stopped = true; | ||
return callback(null); | ||
} | ||
ref = this.connections; | ||
// Disconnect all connections | ||
for (i = 0, len = ref.length; i < len; i++) { | ||
connection = ref[i]; | ||
if (!connection.isConnected()) { | ||
continue; | ||
} | ||
connection.disconnect(); | ||
} | ||
// Emit stop event when all processes are stopped | ||
count = 0; | ||
length = this.processes ? Object.keys(this.processes).length : 0; | ||
onProcessEnd = (err) => { | ||
}, { | ||
key: "removeInitial", | ||
value: function removeInitial(iip, callback) { | ||
var _this7 = this; | ||
_get(_getPrototypeOf(Network.prototype), "removeInitial", this).call(this, iip, function (err) { | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
count++; | ||
if (count === length) { | ||
this.setStarted(false); | ||
this.stopped = true; | ||
return callback(); | ||
} | ||
}; | ||
if (!(this.processes && Object.keys(this.processes).length)) { | ||
this.setStarted(false); | ||
this.stopped = true; | ||
return callback(); | ||
} | ||
ref1 = this.processes; | ||
// Tell processes to shut down | ||
results = []; | ||
for (id in ref1) { | ||
process = ref1[id]; | ||
if (!process.component.isStarted()) { | ||
onProcessEnd(); | ||
continue; | ||
} | ||
if (process.component.shutdown.length === 0) { | ||
platform.deprecated('component.shutdown method without callback is deprecated'); | ||
process.component.shutdown(); | ||
onProcessEnd(); | ||
continue; | ||
} | ||
results.push(process.component.shutdown(onProcessEnd)); | ||
} | ||
return results; | ||
} | ||
setStarted(started) { | ||
if (this.started === started) { | ||
return; | ||
} | ||
if (!started) { | ||
// Ending the execution | ||
this.started = false; | ||
this.bufferedEmit('end', { | ||
start: this.startupDate, | ||
end: new Date, | ||
uptime: this.uptime() | ||
}); | ||
return; | ||
} | ||
if (!this.startupDate) { | ||
// Starting the execution | ||
this.startupDate = new Date; | ||
} | ||
this.started = true; | ||
this.stopped = false; | ||
return this.bufferedEmit('start', { | ||
start: this.startupDate | ||
_this7.graph.removeInitial(iip.to.node, iip.to.port); | ||
callback(); | ||
}); | ||
} | ||
}]); | ||
checkIfFinished() { | ||
if (this.isRunning()) { | ||
return; | ||
} | ||
delete this.abortDebounce; | ||
if (!this.debouncedEnd) { | ||
this.debouncedEnd = utils.debounce(() => { | ||
if (this.abortDebounce) { | ||
return; | ||
} | ||
if (this.isRunning()) { | ||
return; | ||
} | ||
return this.setStarted(false); | ||
}, 50); | ||
} | ||
return this.debouncedEnd(); | ||
} | ||
getDebug() { | ||
return this.debug; | ||
} | ||
setDebug(active) { | ||
var i, instance, len, process, processId, ref, ref1, results, socket; | ||
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; | ||
} | ||
}; | ||
// Processes contains all the instantiated components for this network | ||
Network.prototype.processes = {}; | ||
// Connections contains all the socket connections in the network | ||
Network.prototype.connections = []; | ||
// Initials contains all Initial Information Packets (IIPs) | ||
Network.prototype.initials = []; | ||
// Container to hold sockets that will be sending default data. | ||
Network.prototype.defaults = []; | ||
// The Graph this network is instantiated with | ||
Network.prototype.graph = null; | ||
// Start-up timestamp for the network, used for calculating uptime | ||
Network.prototype.startupDate = null; | ||
return Network; | ||
}(BaseNetwork); | ||
}).call(this); | ||
exports.Network = Network; | ||
exports.Network = Network; |
257
lib/NoFlo.js
@@ -0,1 +1,5 @@ | ||
"use strict"; | ||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
// NoFlo - Flow-Based Programming for JavaScript | ||
@@ -5,41 +9,43 @@ // (c) 2013-2018 Flowhub UG | ||
// NoFlo may be freely distributed under the MIT license | ||
// | ||
// NoFlo is a Flow-Based Programming environment for JavaScript. This file provides the | ||
// main entry point to the NoFlo network. | ||
// | ||
// Find out more about using NoFlo from <http://noflojs.org/documentation/> | ||
/* eslint-disable | ||
no-param-reassign, | ||
*/ | ||
// ## Main APIs | ||
// | ||
// ### Graph interface | ||
// | ||
// [fbp-graph](https://github.com/flowbased/fbp-graph) is used for instantiating FBP graph definitions. | ||
var fbpGraph, ports; | ||
var fbpGraph = require('fbp-graph'); | ||
fbpGraph = require('fbp-graph'); | ||
exports.graph = fbpGraph.graph; | ||
exports.Graph = fbpGraph.Graph; // ### Graph journal | ||
// | ||
// Journal is used for keeping track of graph changes | ||
exports.Graph = fbpGraph.Graph; | ||
// ### Graph journal | ||
// Journal is used for keeping track of graph changes | ||
exports.journal = fbpGraph.journal; | ||
exports.Journal = fbpGraph.Journal; // ## Network interface | ||
// | ||
// [Network](../Network/) is used for running NoFlo graphs. The direct Network inteface is only | ||
// provided for backwards compatibility purposes. Use `createNetwork` instead. | ||
exports.Journal = fbpGraph.Journal; | ||
var _require = require('./Network'), | ||
Network = _require.Network; | ||
// ## Network interface | ||
exports.Network = require('./LegacyNetwork').Network; | ||
// [Network](../Network/) is used for running NoFlo graphs. | ||
exports.Network = require('./Network').Network; | ||
// ### Platform detection | ||
var _require2 = require('./Platform'), | ||
deprecated = _require2.deprecated; // ### Platform detection | ||
// | ||
// NoFlo works on both Node.js and the browser. Because some dependencies are different, | ||
// we need a way to detect which we're on. | ||
exports.isBrowser = require('./Platform').isBrowser; | ||
// ### Component Loader | ||
exports.isBrowser = require('./Platform').isBrowser; // ### Component Loader | ||
// | ||
// The [ComponentLoader](../ComponentLoader/) is responsible for finding and loading | ||
@@ -49,51 +55,38 @@ // NoFlo components. Component Loader uses [fbp-manifest](https://github.com/flowbased/fbp-manifest) | ||
// directory on the file system. | ||
exports.ComponentLoader = require('./ComponentLoader').ComponentLoader; | ||
// ### Component baseclasses | ||
exports.ComponentLoader = require('./ComponentLoader').ComponentLoader; // ### Component baseclasses | ||
// | ||
// These baseclasses can be used for defining NoFlo components. | ||
exports.Component = require('./Component').Component; | ||
// ### Component helpers | ||
exports.Component = require('./Component').Component; // ### NoFlo ports | ||
// | ||
// These classes are used for instantiating ports on NoFlo components. | ||
// These helpers aid in providing specific behavior in components with minimal overhead. | ||
exports.helpers = require('./Helpers'); | ||
var ports = require('./Ports'); | ||
// ### NoFlo ports | ||
// These classes are used for instantiating ports on NoFlo components. | ||
ports = require('./Ports'); | ||
exports.InPorts = ports.InPorts; | ||
exports.OutPorts = ports.OutPorts; | ||
exports.InPort = require('./InPort'); | ||
exports.OutPort = require('./OutPort'); | ||
// ### NoFlo sockets | ||
exports.OutPort = require('./OutPort'); // ### NoFlo sockets | ||
// | ||
// The NoFlo [internalSocket](InternalSocket.html) is used for connecting ports of | ||
// different components together in a network. | ||
exports.internalSocket = require('./InternalSocket'); | ||
// ### Information Packets | ||
exports.internalSocket = require('./InternalSocket'); // ### Information Packets | ||
// | ||
// NoFlo Information Packets are defined as "IP" objects. | ||
exports.IP = require('./IP'); | ||
// ## Network instantiation | ||
exports.IP = require('./IP'); // ## Network instantiation | ||
// | ||
// This function handles instantiation of NoFlo networks from a Graph object. It creates | ||
// the network, and then starts execution by sending the Initial Information Packets. | ||
// | ||
// noflo.createNetwork(someGraph, function (err, network) { | ||
// console.log('Network is now running!'); | ||
// }); | ||
// | ||
// It is also possible to instantiate a Network but delay its execution by giving the | ||
// third `delay` parameter. In this case you will have to handle connecting the graph and | ||
// sending of IIPs manually. | ||
// | ||
// noflo.createNetwork(someGraph, function (err, network) { | ||
@@ -108,5 +101,27 @@ // if (err) { | ||
// }, true); | ||
exports.createNetwork = function(graph, callback, options) { | ||
var network, networkReady; | ||
if (typeof options !== 'object') { | ||
// | ||
// ### Network options | ||
// | ||
// It is possible to pass some options to control the behavior of network creation: | ||
// | ||
// * `delay`: (default: FALSE) Whether the network should be started later. Defaults to | ||
// immediate execution | ||
// * `subscribeGraph`: (default: TRUE) Whether the network should monitor the underlying | ||
// graph for changes | ||
// | ||
// Options can be passed as a second argument before the callback: | ||
// | ||
// noflo.createNetwork(someGraph, options, callback); | ||
// | ||
// The options object can also be used for setting ComponentLoader options in this | ||
// network. | ||
exports.createNetwork = function createNetwork(graph, options, callback) { | ||
if (typeof options === 'function') { | ||
var opts = callback; | ||
callback = options; | ||
options = opts; | ||
} | ||
if (typeof options === 'boolean') { | ||
options = { | ||
@@ -116,4 +131,16 @@ delay: options | ||
} | ||
if (_typeof(options) !== 'object') { | ||
options = {}; | ||
} | ||
if (typeof options.subscribeGraph === 'undefined') { | ||
// Default to legacy network for backwards compatibility. | ||
options.subscribeGraph = true; | ||
} | ||
if (typeof callback !== 'function') { | ||
callback = function(err) { | ||
deprecated('Calling noflo.createNetwork without a callback is deprecated'); | ||
callback = function callback(err) { | ||
if (err) { | ||
@@ -123,43 +150,56 @@ throw err; | ||
}; | ||
} | ||
network = new exports.Network(graph, options); | ||
networkReady = function(network) { | ||
} // Choose legacy or modern network based on whether graph | ||
// subscription is needed | ||
var NetworkType = options.subscribeGraph ? exports.Network : Network; | ||
var network = new NetworkType(graph, options); | ||
var networkReady = function networkReady(net) { | ||
// Send IIPs | ||
return network.start(function(err) { | ||
net.start(function (err) { | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
return callback(null, network); | ||
callback(null, net); | ||
}); | ||
}; | ||
// Ensure components are loaded before continuing | ||
network.loader.listComponents(function(err) { | ||
}; // Ensure components are loaded before continuing | ||
network.loader.listComponents(function (err) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
if (graph.nodes.length === 0) { | ||
// Empty network, no need to connect it up | ||
return networkReady(network); | ||
} | ||
// In case of delayed execution we don't wire it up | ||
callback(err); | ||
return; | ||
} // In case of delayed execution we don't wire it up | ||
if (options.delay) { | ||
callback(null, network); | ||
return; | ||
} | ||
// Wire the network up and start execution | ||
return network.connect(function(err) { | ||
if (err) { | ||
return callback(err); | ||
} // Empty network, no need to connect it up | ||
if (graph.nodes.length === 0) { | ||
networkReady(network); | ||
return; | ||
} // Wire the network up and start execution | ||
network.connect(function (err2) { | ||
if (err2) { | ||
callback(err2); | ||
return; | ||
} | ||
return networkReady(network); | ||
networkReady(network); | ||
}); | ||
}); | ||
return network; | ||
}; | ||
// ### Starting a network from a file | ||
}; // ### Starting a network from a file | ||
// | ||
// It is also possible to start a NoFlo network by giving it a path to a `.json` or `.fbp` network | ||
// definition file. | ||
// | ||
// noflo.loadFile('somefile.json', function (err, network) { | ||
@@ -171,9 +211,11 @@ // if (err) { | ||
// }) | ||
exports.loadFile = function(file, options, callback) { | ||
var baseDir; | ||
exports.loadFile = function loadFile(file, options, callback) { | ||
if (!callback) { | ||
callback = options; | ||
baseDir = null; | ||
options = null; | ||
} | ||
if (callback && typeof options !== 'object') { | ||
if (callback && _typeof(options) !== 'object') { | ||
options = { | ||
@@ -183,28 +225,38 @@ baseDir: options | ||
} | ||
return exports.graph.loadFile(file, function(err, net) { | ||
if (_typeof(options) !== 'object') { | ||
options = {}; | ||
} | ||
if (!options.subscribeGraph) { | ||
options.subscribeGraph = false; | ||
} | ||
exports.graph.loadFile(file, function (err, net) { | ||
if (err) { | ||
return callback(err); | ||
callback(err); | ||
return; | ||
} | ||
if (options.baseDir) { | ||
net.baseDir = options.baseDir; | ||
} | ||
return exports.createNetwork(net, callback, options); | ||
exports.createNetwork(net, options, callback); | ||
}); | ||
}; | ||
// ### Saving a network definition | ||
}; // ### Saving a network definition | ||
// | ||
// NoFlo graph files can be saved back into the filesystem with this method. | ||
exports.saveFile = function(graph, file, callback) { | ||
return graph.save(file, callback); | ||
}; | ||
// ## Embedding NoFlo in existing JavaScript code | ||
exports.saveFile = function saveFile(graph, file, callback) { | ||
graph.save(file, callback); | ||
}; // ## Embedding NoFlo in existing JavaScript code | ||
// | ||
// The `asCallback` helper provides an interface to wrap NoFlo components | ||
// or graphs into existing JavaScript code. | ||
// | ||
// // Produce an asynchronous function wrapping a NoFlo graph | ||
// var wrapped = noflo.asCallback('myproject/MyGraph'); | ||
// | ||
// // Call the function, providing input data and a callback for output data | ||
@@ -216,10 +268,11 @@ // wrapped({ | ||
// }); | ||
exports.asCallback = require('./AsCallback').asCallback; | ||
// | ||
// ## Generating components from JavaScript functions | ||
exports.asCallback = require('./AsCallback').asCallback; // ## Generating components from JavaScript functions | ||
// | ||
// The `asComponent` helper makes it easy to expose a JavaScript function as a | ||
// NoFlo component. All input arguments become input ports, and the function's | ||
// result will be sent to either `out` or `error` port. | ||
// | ||
// exports.getComponent = function () { | ||
@@ -230,2 +283,4 @@ // return noflo.asComponent(Math.random, { | ||
// }; | ||
exports.asComponent = require('./AsComponent').asComponent; | ||
// | ||
exports.asComponent = require('./AsComponent').asComponent; |
@@ -0,184 +1,262 @@ | ||
"use strict"; | ||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } | ||
function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); } | ||
function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } | ||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } | ||
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } | ||
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } | ||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } | ||
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } | ||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } | ||
// NoFlo - Flow-Based Programming for JavaScript | ||
// (c) 2014-2017 Flowhub UG | ||
// NoFlo may be freely distributed under the MIT license | ||
var BasePort, IP, OutPort; | ||
var BasePort = require('./BasePort'); | ||
BasePort = require('./BasePort'); | ||
var IP = require('./IP'); // ## NoFlo outport | ||
// | ||
// Outport Port (outport) implementation for NoFlo components. | ||
// These ports are the way a component sends Information Packets. | ||
IP = require('./IP'); | ||
// ## NoFlo outport | ||
module.exports = /*#__PURE__*/function (_BasePort) { | ||
_inherits(OutPort, _BasePort); | ||
// Outport Port (outport) implementation for NoFlo components. | ||
// These ports are the way a component sends Information Packets. | ||
OutPort = class OutPort extends BasePort { | ||
constructor(options = {}) { | ||
if (options.scoped == null) { | ||
options.scoped = true; | ||
var _super = _createSuper(OutPort); | ||
function OutPort() { | ||
var _this; | ||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
_classCallCheck(this, OutPort); | ||
var opts = options; | ||
if (opts.scoped == null) { | ||
opts.scoped = true; | ||
} | ||
super(options); | ||
this.cache = {}; | ||
} | ||
attach(socket, index = null) { | ||
super.attach(socket, index); | ||
if (this.isCaching() && (this.cache[index] != null)) { | ||
return this.send(this.cache[index], index); | ||
} | ||
_this = _super.call(this, opts); | ||
_this.cache = {}; | ||
return _this; | ||
} | ||
connect(socketId = null) { | ||
var i, len, results, socket, sockets; | ||
sockets = this.getSockets(socketId); | ||
this.checkRequired(sockets); | ||
results = []; | ||
for (i = 0, len = sockets.length; i < len; i++) { | ||
socket = sockets[i]; | ||
if (!socket) { | ||
continue; | ||
_createClass(OutPort, [{ | ||
key: "attach", | ||
value: function attach(socket) { | ||
var index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; | ||
_get(_getPrototypeOf(OutPort.prototype), "attach", this).call(this, socket, index); | ||
if (this.isCaching() && this.cache[index] != null) { | ||
this.send(this.cache[index], index); | ||
} | ||
results.push(socket.connect()); | ||
} | ||
return results; | ||
} | ||
}, { | ||
key: "connect", | ||
value: function connect() { | ||
var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; | ||
var sockets = this.getSockets(index); | ||
this.checkRequired(sockets); | ||
sockets.forEach(function (socket) { | ||
if (!socket) { | ||
return; | ||
} | ||
beginGroup(group, socketId = null) { | ||
var sockets; | ||
sockets = this.getSockets(socketId); | ||
this.checkRequired(sockets); | ||
return sockets.forEach(function(socket) { | ||
if (!socket) { | ||
return; | ||
socket.connect(); | ||
}); | ||
} | ||
}, { | ||
key: "beginGroup", | ||
value: function beginGroup(group) { | ||
var index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; | ||
var sockets = this.getSockets(index); | ||
this.checkRequired(sockets); | ||
sockets.forEach(function (socket) { | ||
if (!socket) { | ||
return; | ||
} | ||
socket.beginGroup(group); | ||
}); | ||
} | ||
}, { | ||
key: "send", | ||
value: function send(data) { | ||
var index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; | ||
var sockets = this.getSockets(index); | ||
this.checkRequired(sockets); | ||
if (this.isCaching() && data !== this.cache[index]) { | ||
this.cache[index] = data; | ||
} | ||
return socket.beginGroup(group); | ||
}); | ||
} | ||
send(data, socketId = null) { | ||
var sockets; | ||
sockets = this.getSockets(socketId); | ||
this.checkRequired(sockets); | ||
if (this.isCaching() && data !== this.cache[socketId]) { | ||
this.cache[socketId] = data; | ||
sockets.forEach(function (socket) { | ||
if (!socket) { | ||
return; | ||
} | ||
socket.send(data); | ||
}); | ||
} | ||
return sockets.forEach(function(socket) { | ||
if (!socket) { | ||
return; | ||
}, { | ||
key: "endGroup", | ||
value: function endGroup() { | ||
var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; | ||
var sockets = this.getSockets(index); | ||
this.checkRequired(sockets); | ||
sockets.forEach(function (socket) { | ||
if (!socket) { | ||
return; | ||
} | ||
socket.endGroup(); | ||
}); | ||
} | ||
}, { | ||
key: "disconnect", | ||
value: function disconnect() { | ||
var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; | ||
var sockets = this.getSockets(index); | ||
this.checkRequired(sockets); | ||
sockets.forEach(function (socket) { | ||
if (!socket) { | ||
return; | ||
} | ||
socket.disconnect(); | ||
}); | ||
} | ||
}, { | ||
key: "sendIP", | ||
value: function sendIP(type, data, options, index) { | ||
var autoConnect = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true; | ||
var ip; | ||
var idx = index; | ||
if (IP.isIP(type)) { | ||
ip = type; | ||
idx = ip.index; | ||
} else { | ||
ip = new IP(type, data, options); | ||
} | ||
return socket.send(data); | ||
}); | ||
} | ||
endGroup(socketId = null) { | ||
var i, len, results, socket, sockets; | ||
sockets = this.getSockets(socketId); | ||
this.checkRequired(sockets); | ||
results = []; | ||
for (i = 0, len = sockets.length; i < len; i++) { | ||
socket = sockets[i]; | ||
if (!socket) { | ||
continue; | ||
var sockets = this.getSockets(idx); | ||
this.checkRequired(sockets); | ||
if (ip.datatype === 'all') { | ||
// Stamp non-specific IP objects with port datatype | ||
ip.datatype = this.getDataType(); | ||
} | ||
results.push(socket.endGroup()); | ||
} | ||
return results; | ||
} | ||
disconnect(socketId = null) { | ||
var i, len, results, socket, sockets; | ||
sockets = this.getSockets(socketId); | ||
this.checkRequired(sockets); | ||
results = []; | ||
for (i = 0, len = sockets.length; i < len; i++) { | ||
socket = sockets[i]; | ||
if (!socket) { | ||
continue; | ||
if (this.getSchema() && !ip.schema) { | ||
// Stamp non-specific IP objects with port schema | ||
ip.schema = this.getSchema(); | ||
} | ||
results.push(socket.disconnect()); | ||
} | ||
return results; | ||
} | ||
sendIP(type, data, options, socketId, autoConnect = true) { | ||
var i, ip, len, pristine, ref, socket, sockets; | ||
if (IP.isIP(type)) { | ||
ip = type; | ||
socketId = ip.index; | ||
} else { | ||
ip = new IP(type, data, options); | ||
var cachedData = this.cache[idx] != null ? this.cache[idx].data : undefined; | ||
if (this.isCaching() && data !== cachedData) { | ||
this.cache[idx] = ip; | ||
} | ||
var pristine = true; | ||
sockets.forEach(function (socket) { | ||
if (!socket) { | ||
return; | ||
} | ||
if (pristine) { | ||
socket.post(ip, autoConnect); | ||
pristine = false; | ||
} else { | ||
if (ip.clonable) { | ||
ip = ip.clone(); | ||
} | ||
socket.post(ip, autoConnect); | ||
} | ||
}); | ||
return this; | ||
} | ||
sockets = this.getSockets(socketId); | ||
this.checkRequired(sockets); | ||
if (ip.datatype === 'all') { | ||
// Stamp non-specific IP objects with port datatype | ||
ip.datatype = this.getDataType(); | ||
}, { | ||
key: "openBracket", | ||
value: function openBracket() { | ||
var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
var index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; | ||
return this.sendIP('openBracket', data, options, index); | ||
} | ||
if (this.getSchema() && !ip.schema) { | ||
// Stamp non-specific IP objects with port schema | ||
ip.schema = this.getSchema(); | ||
}, { | ||
key: "data", | ||
value: function data(_data) { | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
var index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; | ||
return this.sendIP('data', _data, options, index); | ||
} | ||
if (this.isCaching() && data !== ((ref = this.cache[socketId]) != null ? ref.data : void 0)) { | ||
this.cache[socketId] = ip; | ||
}, { | ||
key: "closeBracket", | ||
value: function closeBracket() { | ||
var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; | ||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
var index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; | ||
return this.sendIP('closeBracket', data, options, index); | ||
} | ||
pristine = true; | ||
for (i = 0, len = sockets.length; i < len; i++) { | ||
socket = sockets[i]; | ||
if (!socket) { | ||
continue; | ||
}, { | ||
key: "checkRequired", | ||
value: function checkRequired(sockets) { | ||
if (sockets.length === 0 && this.isRequired()) { | ||
throw new Error("".concat(this.getId(), ": No connections available")); | ||
} | ||
if (pristine) { | ||
socket.post(ip, autoConnect); | ||
pristine = false; | ||
} else { | ||
if (ip.clonable) { | ||
ip = ip.clone(); | ||
} | ||
}, { | ||
key: "getSockets", | ||
value: function getSockets(index) { | ||
// Addressable sockets affect only one connection at time | ||
if (this.isAddressable()) { | ||
if (index === null) { | ||
throw new Error("".concat(this.getId(), " Socket ID required")); | ||
} | ||
socket.post(ip, autoConnect); | ||
} | ||
} | ||
return this; | ||
} | ||
openBracket(data = null, options = {}, socketId = null) { | ||
return this.sendIP('openBracket', data, options, socketId); | ||
} | ||
if (!this.sockets[index]) { | ||
return []; | ||
} | ||
data(data, options = {}, socketId = null) { | ||
return this.sendIP('data', data, options, socketId); | ||
} | ||
return [this.sockets[index]]; | ||
} // Regular sockets affect all outbound connections | ||
closeBracket(data = null, options = {}, socketId = null) { | ||
return this.sendIP('closeBracket', data, options, socketId); | ||
} | ||
checkRequired(sockets) { | ||
if (sockets.length === 0 && this.isRequired()) { | ||
throw new Error(`${this.getId()}: No connections available`); | ||
return this.sockets; | ||
} | ||
} | ||
getSockets(socketId) { | ||
// Addressable sockets affect only one connection at time | ||
if (this.isAddressable()) { | ||
if (socketId === null) { | ||
throw new Error(`${this.getId()} Socket ID required`); | ||
}, { | ||
key: "isCaching", | ||
value: function isCaching() { | ||
if (this.options.caching) { | ||
return true; | ||
} | ||
if (!this.sockets[socketId]) { | ||
return []; | ||
} | ||
return [this.sockets[socketId]]; | ||
} | ||
// Regular sockets affect all outbound connections | ||
return this.sockets; | ||
} | ||
isCaching() { | ||
if (this.options.caching) { | ||
return true; | ||
return false; | ||
} | ||
return false; | ||
} | ||
}]); | ||
}; | ||
module.exports = OutPort; | ||
return OutPort; | ||
}(BasePort); |
@@ -0,17 +1,25 @@ | ||
"use strict"; | ||
// NoFlo - Flow-Based Programming for JavaScript | ||
// (c) 2014-2017 Flowhub UG | ||
// NoFlo may be freely distributed under the MIT license | ||
// | ||
/* eslint-disable | ||
no-console, | ||
no-undef, | ||
*/ | ||
// Platform detection method | ||
exports.isBrowser = function() { | ||
exports.isBrowser = function isBrowser() { | ||
if (typeof process !== 'undefined' && process.execPath && process.execPath.match(/node|iojs/)) { | ||
return false; | ||
} | ||
return true; | ||
}; | ||
// Mechanism for showing API deprecation warnings. By default logs the warnings | ||
}; // Mechanism for showing API deprecation warnings. By default logs the warnings | ||
// but can also be configured to throw instead with the `NOFLO_FATAL_DEPRECATED` | ||
// env var. | ||
exports.deprecated = function(message) { | ||
exports.deprecated = function deprecated(message) { | ||
if (exports.isBrowser()) { | ||
@@ -21,9 +29,12 @@ if (window.NOFLO_FATAL_DEPRECATED) { | ||
} | ||
console.warn(message); | ||
return; | ||
} | ||
if (process.env.NOFLO_FATAL_DEPRECATED) { | ||
throw new Error(message); | ||
} | ||
return console.warn(message); | ||
}; | ||
console.warn(message); | ||
}; |
256
lib/Ports.js
@@ -0,152 +1,230 @@ | ||
"use strict"; | ||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } | ||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } | ||
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } | ||
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } | ||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } | ||
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } | ||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } | ||
/* eslint-disable max-classes-per-file */ | ||
// NoFlo - Flow-Based Programming for JavaScript | ||
// (c) 2014-2017 Flowhub UG | ||
// NoFlo may be freely distributed under the MIT license | ||
var EventEmitter, InPort, InPorts, OutPort, OutPorts, Ports; | ||
var _require = require('events'), | ||
EventEmitter = _require.EventEmitter; | ||
({EventEmitter} = require('events')); | ||
var InPort = require('./InPort'); | ||
InPort = require('./InPort'); | ||
var OutPort = require('./OutPort'); // NoFlo ports collections | ||
// | ||
// Ports collection classes for NoFlo components. These are | ||
// used to hold a set of input or output ports of a component. | ||
OutPort = require('./OutPort'); | ||
Ports = (function() { | ||
// NoFlo ports collections | ||
var Ports = /*#__PURE__*/function (_EventEmitter) { | ||
_inherits(Ports, _EventEmitter); | ||
// Ports collection classes for NoFlo components. These are | ||
// used to hold a set of input or output ports of a component. | ||
class Ports extends EventEmitter { | ||
constructor(ports) { | ||
var name, options; | ||
super(); | ||
this.ports = {}; | ||
if (!ports) { | ||
return; | ||
} | ||
for (name in ports) { | ||
options = ports[name]; | ||
this.add(name, options); | ||
} | ||
var _super = _createSuper(Ports); | ||
function Ports(ports, model) { | ||
var _this; | ||
_classCallCheck(this, Ports); | ||
_this = _super.call(this); | ||
_this.model = model; | ||
_this.ports = {}; | ||
if (!ports) { | ||
return _possibleConstructorReturn(_this); | ||
} | ||
add(name, options, process) { | ||
Object.keys(ports).forEach(function (name) { | ||
var options = ports[name]; | ||
_this.add(name, options); | ||
}); | ||
return _this; | ||
} | ||
_createClass(Ports, [{ | ||
key: "add", | ||
value: function add(name, options, process) { | ||
if (name === 'add' || name === 'remove') { | ||
throw new Error('Add and remove are restricted port names'); | ||
} | ||
/* eslint-disable no-useless-escape */ | ||
if (!name.match(/^[a-z0-9_\.\/]+$/)) { | ||
throw new Error(`Port names can only contain lowercase alphanumeric characters and underscores. '${name}' not allowed`); | ||
} | ||
throw new Error("Port names can only contain lowercase alphanumeric characters and underscores. '".concat(name, "' not allowed")); | ||
} // Remove previous implementation | ||
if (this.ports[name]) { | ||
// Remove previous implementation | ||
this.remove(name); | ||
} | ||
if (typeof options === 'object' && options.canAttach) { | ||
if (_typeof(options) === 'object' && options.canAttach) { | ||
this.ports[name] = options; | ||
} else { | ||
this.ports[name] = new this.model(options, process); | ||
var Model = this.model; | ||
this.ports[name] = new Model(options, process); | ||
} | ||
this[name] = this.ports[name]; | ||
this.emit('add', name); | ||
return this; | ||
return this; // chainable | ||
} | ||
remove(name) { | ||
}, { | ||
key: "remove", | ||
value: function remove(name) { | ||
if (!this.ports[name]) { | ||
throw new Error(`Port ${name} not defined`); | ||
throw new Error("Port ".concat(name, " not defined")); | ||
} | ||
delete this.ports[name]; | ||
delete this[name]; | ||
this.emit('remove', name); | ||
return this; | ||
return this; // chainable | ||
} | ||
}]); | ||
}; | ||
return Ports; | ||
}(EventEmitter); | ||
Ports.prototype.model = InPort; | ||
exports.InPorts = /*#__PURE__*/function (_Ports) { | ||
_inherits(InPorts, _Ports); | ||
return Ports; | ||
var _super2 = _createSuper(InPorts); | ||
}).call(this); | ||
function InPorts(ports) { | ||
_classCallCheck(this, InPorts); | ||
exports.InPorts = InPorts = class InPorts extends Ports { | ||
on(name, event, callback) { | ||
if (!this.ports[name]) { | ||
throw new Error(`Port ${name} not available`); | ||
} | ||
return this.ports[name].on(event, callback); | ||
return _super2.call(this, ports, InPort); | ||
} | ||
once(name, event, callback) { | ||
if (!this.ports[name]) { | ||
throw new Error(`Port ${name} not available`); | ||
_createClass(InPorts, [{ | ||
key: "on", | ||
value: function on(name, event, callback) { | ||
if (!this.ports[name]) { | ||
throw new Error("Port ".concat(name, " not available")); | ||
} | ||
this.ports[name].on(event, callback); | ||
} | ||
return this.ports[name].once(event, callback); | ||
}, { | ||
key: "once", | ||
value: function once(name, event, callback) { | ||
if (!this.ports[name]) { | ||
throw new Error("Port ".concat(name, " not available")); | ||
} | ||
this.ports[name].once(event, callback); | ||
} | ||
}]); | ||
return InPorts; | ||
}(Ports); | ||
exports.OutPorts = /*#__PURE__*/function (_Ports2) { | ||
_inherits(OutPorts, _Ports2); | ||
var _super3 = _createSuper(OutPorts); | ||
function OutPorts(ports) { | ||
_classCallCheck(this, OutPorts); | ||
return _super3.call(this, ports, OutPort); | ||
} | ||
}; | ||
exports.OutPorts = OutPorts = (function() { | ||
class OutPorts extends Ports { | ||
connect(name, socketId) { | ||
_createClass(OutPorts, [{ | ||
key: "connect", | ||
value: function connect(name, socketId) { | ||
if (!this.ports[name]) { | ||
throw new Error(`Port ${name} not available`); | ||
throw new Error("Port ".concat(name, " not available")); | ||
} | ||
return this.ports[name].connect(socketId); | ||
this.ports[name].connect(socketId); | ||
} | ||
beginGroup(name, group, socketId) { | ||
}, { | ||
key: "beginGroup", | ||
value: function beginGroup(name, group, socketId) { | ||
if (!this.ports[name]) { | ||
throw new Error(`Port ${name} not available`); | ||
throw new Error("Port ".concat(name, " not available")); | ||
} | ||
return this.ports[name].beginGroup(group, socketId); | ||
this.ports[name].beginGroup(group, socketId); | ||
} | ||
send(name, data, socketId) { | ||
}, { | ||
key: "send", | ||
value: function send(name, data, socketId) { | ||
if (!this.ports[name]) { | ||
throw new Error(`Port ${name} not available`); | ||
throw new Error("Port ".concat(name, " not available")); | ||
} | ||
return this.ports[name].send(data, socketId); | ||
this.ports[name].send(data, socketId); | ||
} | ||
endGroup(name, socketId) { | ||
}, { | ||
key: "endGroup", | ||
value: function endGroup(name, socketId) { | ||
if (!this.ports[name]) { | ||
throw new Error(`Port ${name} not available`); | ||
throw new Error("Port ".concat(name, " not available")); | ||
} | ||
return this.ports[name].endGroup(socketId); | ||
this.ports[name].endGroup(socketId); | ||
} | ||
disconnect(name, socketId) { | ||
}, { | ||
key: "disconnect", | ||
value: function disconnect(name, socketId) { | ||
if (!this.ports[name]) { | ||
throw new Error(`Port ${name} not available`); | ||
throw new Error("Port ".concat(name, " not available")); | ||
} | ||
return this.ports[name].disconnect(socketId); | ||
this.ports[name].disconnect(socketId); | ||
} | ||
}]); | ||
}; | ||
OutPorts.prototype.model = OutPort; | ||
return OutPorts; | ||
}(Ports); // Port name normalization: | ||
// returns object containing keys name and index for ports names in | ||
// format `portname` or `portname[index]`. | ||
}).call(this); | ||
// Port name normalization: | ||
// returns object containing keys name and index for ports names in | ||
// format `portname` or `portname[index]`. | ||
exports.normalizePortName = function(name) { | ||
var matched, port; | ||
port = { | ||
exports.normalizePortName = function normalizePortName(name) { | ||
var port = { | ||
name: name | ||
}; | ||
}; // Regular port | ||
if (name.indexOf('[') === -1) { | ||
// Regular port | ||
return port; | ||
} | ||
// Addressable port with index | ||
matched = name.match(/(.*)\[([0-9]+)\]/); | ||
if (!(matched != null ? matched.length : void 0)) { | ||
} // Addressable port with index | ||
var matched = name.match(/(.*)\[([0-9]+)\]/); | ||
if (!(matched != null ? matched.length : undefined)) { | ||
return name; | ||
} | ||
port.name = matched[1]; | ||
port.index = matched[2]; | ||
return port; | ||
}; | ||
return { | ||
name: matched[1], | ||
index: matched[2] | ||
}; | ||
}; |
134
lib/Utils.js
@@ -0,1 +1,3 @@ | ||
"use strict"; | ||
// NoFlo - Flow-Based Programming for JavaScript | ||
@@ -5,74 +7,88 @@ // (c) 2014-2017 Flowhub UG | ||
/* eslint-disable | ||
no-param-reassign, | ||
prefer-rest-params, | ||
*/ | ||
// Guess language from filename | ||
var createReduce, debounce, guessLanguageFromFilename, isArray, optimizeCb, reduceRight; | ||
guessLanguageFromFilename = function(filename) { | ||
function guessLanguageFromFilename(filename) { | ||
if (/.*\.coffee$/.test(filename)) { | ||
return 'coffeescript'; | ||
} | ||
return 'javascript'; | ||
}; | ||
} | ||
isArray = function(obj) { | ||
function isArray(obj) { | ||
if (Array.isArray) { | ||
return Array.isArray(obj); | ||
} | ||
return Object.prototype.toString.call(arg) === '[object Array]'; | ||
}; | ||
// the following functions are from http://underscorejs.org/docs/underscore.html | ||
return Object.prototype.toString.call(obj) === '[object Array]'; | ||
} // the following functions are from http://underscorejs.org/docs/underscore.html | ||
// Underscore.js 1.8.3 http://underscorejs.org | ||
// (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors | ||
// Underscore may be freely distributed under the MIT license. | ||
// Internal function that returns an efficient (for current engines) | ||
// version of the passed-in callback, | ||
// to be repeatedly applied in other Underscore functions. | ||
optimizeCb = function(func, context, argCount) { | ||
if (context === void 0) { | ||
function optimizeCb(func, context, argCount) { | ||
if (context === undefined) { | ||
return func; | ||
} | ||
switch ((argCount === null ? 3 : argCount)) { | ||
switch (argCount === null ? 3 : argCount) { | ||
case 1: | ||
return function(value) { | ||
return function (value) { | ||
return func.call(context, value); | ||
}; | ||
case 2: | ||
return function(value, other) { | ||
return function (value, other) { | ||
return func.call(context, value, other); | ||
}; | ||
case 3: | ||
return function(value, index, collection) { | ||
return function (value, index, collection) { | ||
return func.call(context, value, index, collection); | ||
}; | ||
case 4: | ||
return function(accumulator, value, index, collection) { | ||
return func.call(context, accumulator, value, index, collection); | ||
return function (accumulator, value, index, collection) { | ||
func.call(context, accumulator, value, index, collection); | ||
}; | ||
default: // No-op | ||
} | ||
return function() { | ||
return function call() { | ||
return func.apply(context, arguments); | ||
}; | ||
}; | ||
// Create a reducing function iterating left or right. | ||
} // Create a reducing function iterating left or right. | ||
// Optimized iterator function as using arguments.length in the main function | ||
// will deoptimize the, see #1991. | ||
createReduce = function(dir) { | ||
var iterator; | ||
iterator = function(obj, iteratee, memo, keys, index, length) { | ||
var currentKey; | ||
function createReduce(dir) { | ||
function iterator(obj, iteratee, memo, keys, index, length) { | ||
while (index >= 0 && index < length) { | ||
currentKey = keys ? keys[index] : index; | ||
var currentKey = keys ? keys[index] : index; | ||
memo = iteratee(memo, obj[currentKey], currentKey, obj); | ||
index += dir; | ||
} | ||
return memo; | ||
}; | ||
return function(obj, iteratee, memo, context) { | ||
var index, keys, length; | ||
} | ||
return function reduce(obj, iteratee, memo, context) { | ||
iteratee = optimizeCb(iteratee, context, 4); | ||
keys = Object.keys(obj); | ||
length = (keys || obj).length; | ||
index = dir > 0 ? 0 : length - 1; | ||
var keys = Object.keys(obj); | ||
var _ref = keys || obj, | ||
length = _ref.length; | ||
var index = dir > 0 ? 0 : length - 1; | ||
if (arguments.length < 3) { | ||
@@ -82,9 +98,8 @@ memo = obj[keys ? keys[index] : index]; | ||
} | ||
return iterator(obj, iteratee, memo, keys, index, length); | ||
}; | ||
}; | ||
} | ||
reduceRight = createReduce(-1); | ||
// Returns a function, that, as long as it continues to be invoked, | ||
var reduceRight = createReduce(-1); // Returns a function, that, as long as it continues to be invoked, | ||
// will not be triggered. | ||
@@ -94,12 +109,13 @@ // The function will be called after it stops being called for N milliseconds. | ||
// instead of the trailing. | ||
debounce = function(func, wait, immediate) { | ||
var args, context, later, result, timeout, timestamp; | ||
timeout = void 0; | ||
args = void 0; | ||
context = void 0; | ||
timestamp = void 0; | ||
result = void 0; | ||
later = function() { | ||
var last; | ||
last = Date.now - timestamp; | ||
function debounce(func, wait, immediate) { | ||
var timeout; | ||
var args; | ||
var context; | ||
var timestamp; | ||
var result; | ||
function later() { | ||
var last = Date.now - timestamp; | ||
if (last < wait && last >= 0) { | ||
@@ -109,33 +125,37 @@ timeout = setTimeout(later, wait - last); | ||
timeout = null; | ||
if (!immediate) { | ||
result = func.apply(context, args); | ||
if (!timeout) { | ||
context = args = null; | ||
context = null; | ||
args = null; | ||
} | ||
} | ||
} | ||
}; | ||
return function() { | ||
var callNow; | ||
} | ||
return function after() { | ||
context = this; | ||
args = arguments; | ||
timestamp = Date.now; | ||
callNow = immediate && !timeout; | ||
var callNow = immediate && !timeout; | ||
if (!timeout) { | ||
timeout = setTimeout(later, wait); | ||
} | ||
if (callNow) { | ||
result = func.apply(context, args); | ||
context = args = null; | ||
context = null; | ||
args = null; | ||
} | ||
return result; | ||
}; | ||
}; | ||
} | ||
exports.guessLanguageFromFilename = guessLanguageFromFilename; | ||
exports.reduceRight = reduceRight; | ||
exports.debounce = debounce; | ||
exports.isArray = isArray; | ||
exports.isArray = isArray; |
@@ -11,3 +11,3 @@ { | ||
"author": "Henri Bergius <henri.bergius@iki.fi>", | ||
"version": "1.1.3", | ||
"version": "1.2.0", | ||
"license": "MIT", | ||
@@ -19,26 +19,27 @@ "engines": { | ||
"coffeescript": "^2.2.1", | ||
"debug": "^3.0.0", | ||
"debug": "^4.0.1", | ||
"fbp": "^1.5.0", | ||
"fbp-graph": "^0.3.1", | ||
"fbp-graph": "^0.4.0", | ||
"fbp-manifest": "^0.2.0", | ||
"get-function-params": "^2.0.3" | ||
"get-function-params": "^2.0.6" | ||
}, | ||
"devDependencies": { | ||
"babel-core": "^6.26.0", | ||
"babel-loader": "^7.1.2", | ||
"babel-preset-es2015": "^6.24.1", | ||
"@babel/cli": "^7.10.5", | ||
"@babel/core": "^7.11.1", | ||
"@babel/preset-env": "^7.11.0", | ||
"chai": "^4.0.0", | ||
"coffee-coverage": "^3.0.0", | ||
"coveralls": "^3.0.0", | ||
"grunt": "^1.0.1", | ||
"grunt-cli": "^1.2.0", | ||
"grunt-coffeelint": "^0.0.16", | ||
"grunt-contrib-coffee": "^2.0.0", | ||
"grunt-contrib-connect": "^1.0.1", | ||
"grunt-contrib-watch": "^1.0.0", | ||
"grunt-mocha-phantomjs": "^4.0.0", | ||
"grunt-mocha-test": "^0.13.2", | ||
"grunt-noflo-browser": "^1.5.2", | ||
"mocha": "^5.0.0", | ||
"nyc": "^11.2.1" | ||
"eslint": "^7.7.0", | ||
"eslint-config-airbnb-base": "^14.2.0", | ||
"eslint-plugin-import": "^2.22.0", | ||
"karma": "^5.1.1", | ||
"karma-chai": "^0.1.0", | ||
"karma-chrome-launcher": "^3.1.0", | ||
"karma-mocha": "^2.0.1", | ||
"karma-mocha-reporter": "^2.2.5", | ||
"mocha": "^7.2.0", | ||
"noflo-component-loader": "^0.3.2", | ||
"nyc": "^15.1.0", | ||
"webpack": "^4.44.1", | ||
"webpack-cli": "^3.3.12" | ||
}, | ||
@@ -56,5 +57,9 @@ "main": "./lib/NoFlo", | ||
"scripts": { | ||
"test": "nyc grunt test", | ||
"posttest": "nyc check-coverage --lines 75", | ||
"build": "grunt build" | ||
"pretest": "eslint src", | ||
"test:node": "nyc mocha --require spec/utils/inject.js spec", | ||
"test:browser": "karma start karma.config.js", | ||
"test": "npm run test:node && npm run test:browser", | ||
"build:node": "babel src --out-dir .", | ||
"build:browser": "webpack", | ||
"build": "npm run build:node && npm run build:browser" | ||
}, | ||
@@ -67,5 +72,5 @@ "docco_husky": { | ||
"include": [ | ||
"src/**/*.coffee" | ||
"src/**/*.js" | ||
] | ||
} | ||
} |
@@ -85,2 +85,3 @@ NoFlo: Flow-based programming for JavaScript [![Build Status](https://secure.travis-ci.org/noflo/noflo.png?branch=master)](http://travis-ci.org/noflo/noflo) [![Build status](https://ci.appveyor.com/api/projects/status/k4jbqlpohq81pvny/branch/master)](https://ci.appveyor.com/project/bergie/noflo/branch/master) [![Coverage Status](https://coveralls.io/repos/github/noflo/noflo/badge.svg?branch=master)](https://coveralls.io/github/noflo/noflo?branch=master) | ||
```bash | ||
$ npm run build | ||
$ npm test | ||
@@ -94,3 +95,3 @@ ``` | ||
```bash | ||
$ grunt test:nodejs | ||
$ npm run test:node | ||
``` | ||
@@ -101,23 +102,7 @@ | ||
```bash | ||
$ grunt test:browser | ||
$ npm run test:browser | ||
``` | ||
### Running tests automatically | ||
The build system used for NoFlo is also able to watch for changes in the filesystem and run the tests automatically when something changes. To start the watcher, run: | ||
```bash | ||
$ grunt watch | ||
``` | ||
If you want to only run a particular part of the test suite, you can filter them using the `TESTS` environment variable: | ||
```bash | ||
$ TESTS="Network Lifecycle" grunt watch | ||
``` | ||
To quit the watcher, just end the process with Ctrl-C. | ||
## Discussion | ||
There is an IRC channel `#fbp` on FreeNode, and questions can be posted with the [`noflo` tag on Stack Overflow](http://stackoverflow.com/questions/tagged/noflo). See <http://noflojs.org/support/> for other ways to get in touch. | ||
There is a `#noflo` channel on the [Flow-Based Programming Slack](https://join.slack.com/t/fbphq/shared_invite/enQtOTM4ODkzMTYyODE3LTJiMmNlZjhiMWY1MDY1ODA4Y2YzNDBlNDZlMTBkMDNlMjcwNzg2MGZhZjA2NjJjYTliYTM0OTIyYmM0Yzk0MDQ), and questions can be posted with the [`noflo` tag on Stack Overflow](http://stackoverflow.com/questions/tagged/noflo). See <http://noflojs.org/support/> for other ways to get in touch. |
@@ -5,7 +5,7 @@ { | ||
"icon": "car", | ||
"loader": "loader.coffee", | ||
"loader": "loader.js", | ||
"components": { | ||
"Forward": "components/Forward.coffee" | ||
"Forward": "components/Forward.js" | ||
} | ||
} | ||
} | ||
} |
@@ -6,3 +6,3 @@ { | ||
"components": { | ||
"Output": "components/Output.coffee" | ||
"Output": "components/Output.js" | ||
} | ||
@@ -13,2 +13,2 @@ }, | ||
} | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
756585
92
20816
18
106
13
2
+ Addeddebug@4.3.7(transitive)
+ Addedfbp-graph@0.4.0(transitive)
- Removeddebug@3.2.7(transitive)
- Removedfbp-graph@0.3.2(transitive)
Updateddebug@^4.0.1
Updatedfbp-graph@^0.4.0
Updatedget-function-params@^2.0.6