noflo
Version:
Flow-Based Programming environment for JavaScript
763 lines (750 loc) • 26.7 kB
JavaScript
(function() {
var InternalSocket, StreamReceiver, StreamSender, isArray, _,
__hasProp = {}.hasOwnProperty;
_ = require('underscore');
StreamSender = require('./Streams').StreamSender;
StreamReceiver = require('./Streams').StreamReceiver;
InternalSocket = require('./InternalSocket');
isArray = function(obj) {
if (Array.isArray) {
return Array.isArray(obj);
}
return Object.prototype.toString.call(arg) === '[object Array]';
};
exports.MapComponent = function(component, func, config) {
var groups, inPort, outPort;
if (!config) {
config = {};
}
if (!config.inPort) {
config.inPort = 'in';
}
if (!config.outPort) {
config.outPort = 'out';
}
inPort = component.inPorts[config.inPort];
outPort = component.outPorts[config.outPort];
groups = [];
return inPort.process = function(event, payload) {
switch (event) {
case 'connect':
return outPort.connect();
case 'begingroup':
groups.push(payload);
return outPort.beginGroup(payload);
case 'data':
return func(payload, groups, outPort);
case 'endgroup':
groups.pop();
return outPort.endGroup();
case 'disconnect':
groups = [];
return outPort.disconnect();
}
};
};
exports.WirePattern = function(component, config, proc) {
var baseShutdown, closeGroupOnOuts, collectGroups, disconnectOuts, gc, inPorts, name, outPorts, port, processQueue, resumeTaskQ, sendGroupToOuts, _fn, _fn1, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m, _ref, _ref1;
inPorts = 'in' in config ? config["in"] : 'in';
if (!isArray(inPorts)) {
inPorts = [inPorts];
}
outPorts = 'out' in config ? config.out : 'out';
if (!isArray(outPorts)) {
outPorts = [outPorts];
}
if (!('error' in config)) {
config.error = 'error';
}
if (!('async' in config)) {
config.async = false;
}
if (!('ordered' in config)) {
config.ordered = true;
}
if (!('group' in config)) {
config.group = false;
}
if (!('field' in config)) {
config.field = null;
}
if (!('forwardGroups' in config)) {
config.forwardGroups = false;
}
if (!('receiveStreams' in config)) {
config.receiveStreams = false;
}
if (typeof config.receiveStreams === 'string') {
config.receiveStreams = [config.receiveStreams];
}
if (!('sendStreams' in config)) {
config.sendStreams = false;
}
if (typeof config.sendStreams === 'string') {
config.sendStreams = [config.sendStreams];
}
if (config.async) {
config.sendStreams = outPorts;
}
if (!('params' in config)) {
config.params = [];
}
if (typeof config.params === 'string') {
config.params = [config.params];
}
if (!('name' in config)) {
config.name = '';
}
if (!('dropInput' in config)) {
config.dropInput = false;
}
if (!('arrayPolicy' in config)) {
config.arrayPolicy = {
"in": 'any',
params: 'all'
};
}
if (!('gcFrequency' in config)) {
config.gcFrequency = 100;
}
if (!('gcTimeout' in config)) {
config.gcTimeout = 300;
}
collectGroups = config.forwardGroups;
if (typeof collectGroups === 'boolean' && !config.group) {
collectGroups = inPorts;
}
if (typeof collectGroups === 'string' && !config.group) {
collectGroups = [collectGroups];
}
if (collectGroups !== false && config.group) {
collectGroups = true;
}
for (_i = 0, _len = inPorts.length; _i < _len; _i++) {
name = inPorts[_i];
if (!component.inPorts[name]) {
throw new Error("no inPort named '" + name + "'");
}
}
for (_j = 0, _len1 = outPorts.length; _j < _len1; _j++) {
name = outPorts[_j];
if (!component.outPorts[name]) {
throw new Error("no outPort named '" + name + "'");
}
}
component.groupedData = {};
component.groupedGroups = {};
component.groupedDisconnects = {};
disconnectOuts = function() {
var p, _k, _len2, _results;
_results = [];
for (_k = 0, _len2 = outPorts.length; _k < _len2; _k++) {
p = outPorts[_k];
if (component.outPorts[p].isConnected()) {
_results.push(component.outPorts[p].disconnect());
} else {
_results.push(void 0);
}
}
return _results;
};
sendGroupToOuts = function(grp) {
var p, _k, _len2, _results;
_results = [];
for (_k = 0, _len2 = outPorts.length; _k < _len2; _k++) {
p = outPorts[_k];
_results.push(component.outPorts[p].beginGroup(grp));
}
return _results;
};
closeGroupOnOuts = function(grp) {
var p, _k, _len2, _results;
_results = [];
for (_k = 0, _len2 = outPorts.length; _k < _len2; _k++) {
p = outPorts[_k];
_results.push(component.outPorts[p].endGroup(grp));
}
return _results;
};
component.outputQ = [];
processQueue = function() {
var flushed, key, stream, streams, tmp;
while (component.outputQ.length > 0) {
streams = component.outputQ[0];
flushed = false;
if (streams === null) {
disconnectOuts();
flushed = true;
} else {
if (outPorts.length === 1) {
tmp = {};
tmp[outPorts[0]] = streams;
streams = tmp;
}
for (key in streams) {
stream = streams[key];
if (stream.resolved) {
stream.flush();
flushed = true;
}
}
}
if (flushed) {
component.outputQ.shift();
}
if (!flushed) {
return;
}
}
};
if (config.async) {
if ('load' in component.outPorts) {
component.load = 0;
}
component.beforeProcess = function(outs) {
if (config.ordered) {
component.outputQ.push(outs);
}
component.load++;
if ('load' in component.outPorts && component.outPorts.load.isAttached()) {
component.outPorts.load.send(component.load);
return component.outPorts.load.disconnect();
}
};
component.afterProcess = function(err, outs) {
processQueue();
component.load--;
if ('load' in component.outPorts && component.outPorts.load.isAttached()) {
component.outPorts.load.send(component.load);
return component.outPorts.load.disconnect();
}
};
}
component.taskQ = [];
component.params = {};
component.requiredParams = [];
component.completeParams = [];
component.receivedParams = [];
component.defaultedParams = [];
component.defaultsSent = false;
component.sendDefaults = function() {
var param, tempSocket, _k, _len2, _ref;
if (component.defaultedParams.length > 0) {
_ref = component.defaultedParams;
for (_k = 0, _len2 = _ref.length; _k < _len2; _k++) {
param = _ref[_k];
if (component.receivedParams.indexOf(param) === -1) {
tempSocket = InternalSocket.createSocket();
component.inPorts[param].attach(tempSocket);
tempSocket.send();
tempSocket.disconnect();
component.inPorts[param].detach(tempSocket);
}
}
}
return component.defaultsSent = true;
};
resumeTaskQ = function() {
var task, temp, _results;
if (component.completeParams.length === component.requiredParams.length && component.taskQ.length > 0) {
temp = component.taskQ.slice(0);
component.taskQ = [];
_results = [];
while (temp.length > 0) {
task = temp.shift();
_results.push(task());
}
return _results;
}
};
_ref = config.params;
for (_k = 0, _len2 = _ref.length; _k < _len2; _k++) {
port = _ref[_k];
if (!component.inPorts[port]) {
throw new Error("no inPort named '" + port + "'");
}
if (component.inPorts[port].isRequired()) {
component.requiredParams.push(port);
}
if (component.inPorts[port].hasDefault()) {
component.defaultedParams.push(port);
}
}
_ref1 = config.params;
_fn = function(port) {
var inPort;
inPort = component.inPorts[port];
return inPort.process = function(event, payload, index) {
if (event !== 'data') {
return;
}
if (inPort.isAddressable()) {
if (!(port in component.params)) {
component.params[port] = {};
}
component.params[port][index] = payload;
if (config.arrayPolicy.params === 'all' && Object.keys(component.params[port]).length < inPort.listAttached().length) {
return;
}
} else {
component.params[port] = payload;
}
if (component.completeParams.indexOf(port) === -1 && component.requiredParams.indexOf(port) > -1) {
component.completeParams.push(port);
}
component.receivedParams.push(port);
return resumeTaskQ();
};
};
for (_l = 0, _len3 = _ref1.length; _l < _len3; _l++) {
port = _ref1[_l];
_fn(port);
}
component.disconnectData = {};
component.disconnectQ = [];
component.groupBuffers = {};
component.keyBuffers = {};
component.gcTimestamps = {};
component.dropRequest = function(key) {
if (key in component.disconnectData) {
delete component.disconnectData[key];
}
if (key in component.groupedData) {
delete component.groupedData[key];
}
if (key in component.groupedGroups) {
return delete component.groupedGroups[key];
}
};
component.gcCounter = 0;
gc = function() {
var current, key, val, _ref2, _results;
component.gcCounter++;
if (component.gcCounter % config.gcFrequency === 0) {
current = new Date().getTime();
_ref2 = component.gcTimestamps;
_results = [];
for (key in _ref2) {
val = _ref2[key];
if ((current - val) > (config.gcTimeout * 1000)) {
component.dropRequest(key);
_results.push(delete component.gcTimestamps[key]);
} else {
_results.push(void 0);
}
}
return _results;
}
};
_fn1 = function(port) {
var inPort, needPortGroups;
component.groupBuffers[port] = [];
component.keyBuffers[port] = null;
if (config.receiveStreams && config.receiveStreams.indexOf(port) !== -1) {
inPort = new StreamReceiver(component.inPorts[port]);
} else {
inPort = component.inPorts[port];
}
needPortGroups = collectGroups instanceof Array && collectGroups.indexOf(port) !== -1;
return inPort.process = function(event, payload, index) {
var data, foundGroup, g, groupLength, groups, grp, i, key, obj, out, outs, postpone, postponedToQ, reqId, requiredLength, resume, task, tmp, whenDone, whenDoneGroups, _len5, _len6, _len7, _len8, _n, _o, _p, _q, _r, _ref2, _ref3, _ref4, _s;
if (!component.groupBuffers[port]) {
component.groupBuffers[port] = [];
}
switch (event) {
case 'begingroup':
component.groupBuffers[port].push(payload);
if (config.forwardGroups && (collectGroups === true || needPortGroups) && !config.async) {
return sendGroupToOuts(payload);
}
break;
case 'endgroup':
component.groupBuffers[port] = component.groupBuffers[port].slice(0, component.groupBuffers[port].length - 1);
if (config.forwardGroups && (collectGroups === true || needPortGroups) && !config.async) {
return closeGroupOnOuts(payload);
}
break;
case 'disconnect':
if (inPorts.length === 1) {
if (config.async || config.StreamSender) {
if (config.ordered) {
component.outputQ.push(null);
return processQueue();
} else {
return component.disconnectQ.push(true);
}
} else {
return disconnectOuts();
}
} else {
foundGroup = false;
key = component.keyBuffers[port];
if (!(key in component.disconnectData)) {
component.disconnectData[key] = [];
}
for (i = _n = 0, _ref2 = component.disconnectData[key].length; 0 <= _ref2 ? _n < _ref2 : _n > _ref2; i = 0 <= _ref2 ? ++_n : --_n) {
if (!(port in component.disconnectData[key][i])) {
foundGroup = true;
component.disconnectData[key][i][port] = true;
if (Object.keys(component.disconnectData[key][i]).length === inPorts.length) {
component.disconnectData[key].shift();
if (config.async || config.StreamSender) {
if (config.ordered) {
component.outputQ.push(null);
processQueue();
} else {
component.disconnectQ.push(true);
}
} else {
disconnectOuts();
}
if (component.disconnectData[key].length === 0) {
delete component.disconnectData[key];
}
}
break;
}
}
if (!foundGroup) {
obj = {};
obj[port] = true;
return component.disconnectData[key].push(obj);
}
}
break;
case 'data':
if (inPorts.length === 1 && !inPort.isAddressable()) {
data = payload;
groups = component.groupBuffers[port];
} else {
key = '';
if (config.group && component.groupBuffers[port].length > 0) {
key = component.groupBuffers[port].toString();
if (config.group instanceof RegExp) {
reqId = null;
_ref3 = component.groupBuffers[port];
for (_o = 0, _len5 = _ref3.length; _o < _len5; _o++) {
grp = _ref3[_o];
if (config.group.test(grp)) {
reqId = grp;
break;
}
}
key = reqId ? reqId : '';
}
} else if (config.field && typeof payload === 'object' && config.field in payload) {
key = payload[config.field];
}
component.keyBuffers[port] = key;
if (!(key in component.groupedData)) {
component.groupedData[key] = [];
}
if (!(key in component.groupedGroups)) {
component.groupedGroups[key] = [];
}
foundGroup = false;
requiredLength = inPorts.length;
if (config.field) {
++requiredLength;
}
for (i = _p = 0, _ref4 = component.groupedData[key].length; 0 <= _ref4 ? _p < _ref4 : _p > _ref4; i = 0 <= _ref4 ? ++_p : --_p) {
if (!(port in component.groupedData[key][i]) || (component.inPorts[port].isAddressable() && config.arrayPolicy["in"] === 'all' && !(index in component.groupedData[key][i][port]))) {
foundGroup = true;
if (component.inPorts[port].isAddressable()) {
if (!(port in component.groupedData[key][i])) {
component.groupedData[key][i][port] = {};
}
component.groupedData[key][i][port][index] = payload;
} else {
component.groupedData[key][i][port] = payload;
}
if (needPortGroups) {
component.groupedGroups[key][i] = _.union(component.groupedGroups[key][i], component.groupBuffers[port]);
} else if (collectGroups === true) {
component.groupedGroups[key][i][port] = component.groupBuffers[port];
}
if (component.inPorts[port].isAddressable() && config.arrayPolicy["in"] === 'all' && Object.keys(component.groupedData[key][i][port]).length < component.inPorts[port].listAttached().length) {
return;
}
groupLength = Object.keys(component.groupedData[key][i]).length;
if (groupLength === requiredLength) {
data = (component.groupedData[key].splice(i, 1))[0];
if (inPorts.length === 1 && inPort.isAddressable()) {
data = data[port];
}
groups = (component.groupedGroups[key].splice(i, 1))[0];
if (collectGroups === true) {
groups = _.intersection.apply(null, _.values(groups));
}
if (component.groupedData[key].length === 0) {
delete component.groupedData[key];
}
if (component.groupedGroups[key].length === 0) {
delete component.groupedGroups[key];
}
if (config.group && key) {
delete component.gcTimestamps[key];
}
break;
} else {
return;
}
}
}
if (!foundGroup) {
obj = {};
if (config.field) {
obj[config.field] = key;
}
if (component.inPorts[port].isAddressable()) {
obj[port] = {};
obj[port][index] = payload;
} else {
obj[port] = payload;
}
if (inPorts.length === 1 && component.inPorts[port].isAddressable() && (config.arrayPolicy["in"] === 'any' || component.inPorts[port].listAttached().length === 1)) {
data = obj[port];
groups = component.groupBuffers[port];
} else {
component.groupedData[key].push(obj);
if (needPortGroups) {
component.groupedGroups[key].push(component.groupBuffers[port]);
} else if (collectGroups === true) {
tmp = {};
tmp[port] = component.groupBuffers[port];
component.groupedGroups[key].push(tmp);
} else {
component.groupedGroups[key].push([]);
}
if (config.group && key) {
component.gcTimestamps[key] = new Date().getTime();
}
return;
}
}
}
if (config.dropInput && component.completeParams.length !== component.requiredParams.length) {
return;
}
outs = {};
for (_q = 0, _len6 = outPorts.length; _q < _len6; _q++) {
name = outPorts[_q];
if (config.async || config.sendStreams && config.sendStreams.indexOf(name) !== -1) {
outs[name] = new StreamSender(component.outPorts[name], config.ordered);
} else {
outs[name] = component.outPorts[name];
}
}
if (outPorts.length === 1) {
outs = outs[outPorts[0]];
}
if (!groups) {
groups = [];
}
whenDoneGroups = groups.slice(0);
whenDone = function(err) {
var disconnect, out, outputs, _len7, _r;
if (err) {
component.error(err, whenDoneGroups);
}
if (typeof component.fail === 'function' && component.hasErrors) {
component.fail();
}
outputs = outPorts.length === 1 ? {
port: outs
} : outs;
disconnect = false;
if (component.disconnectQ.length > 0) {
component.disconnectQ.shift();
disconnect = true;
}
for (name in outputs) {
out = outputs[name];
if (config.forwardGroups && config.async) {
for (_r = 0, _len7 = whenDoneGroups.length; _r < _len7; _r++) {
i = whenDoneGroups[_r];
out.endGroup();
}
}
if (disconnect) {
out.disconnect();
}
if (config.async || config.StreamSender) {
out.done();
}
}
if (typeof component.afterProcess === 'function') {
return component.afterProcess(err || component.hasErrors, outs);
}
};
if (typeof component.beforeProcess === 'function') {
component.beforeProcess(outs);
}
if (config.forwardGroups && config.async) {
if (outPorts.length === 1) {
for (_r = 0, _len7 = groups.length; _r < _len7; _r++) {
g = groups[_r];
outs.beginGroup(g);
}
} else {
for (name in outs) {
out = outs[name];
for (_s = 0, _len8 = groups.length; _s < _len8; _s++) {
g = groups[_s];
out.beginGroup(g);
}
}
}
}
exports.MultiError(component, config.name, config.error, groups);
if (config.async) {
postpone = function() {};
resume = function() {};
postponedToQ = false;
task = function() {
return proc.call(component, data, groups, outs, whenDone, postpone, resume);
};
postpone = function(backToQueue) {
if (backToQueue == null) {
backToQueue = true;
}
postponedToQ = backToQueue;
if (backToQueue) {
return component.taskQ.push(task);
}
};
resume = function() {
if (postponedToQ) {
return resumeTaskQ();
} else {
return task();
}
};
} else {
task = function() {
proc.call(component, data, groups, outs);
return whenDone();
};
}
component.taskQ.push(task);
resumeTaskQ();
return gc();
}
};
};
for (_m = 0, _len4 = inPorts.length; _m < _len4; _m++) {
port = inPorts[_m];
_fn1(port);
}
baseShutdown = component.shutdown;
component.shutdown = function() {
baseShutdown.call(component);
component.groupedData = {};
component.groupedGroups = {};
component.outputQ = [];
component.disconnectData = {};
component.disconnectQ = [];
component.taskQ = [];
component.params = {};
component.completeParams = [];
component.receivedParams = [];
component.defaultsSent = false;
component.groupBuffers = {};
component.keyBuffers = {};
component.gcTimestamps = {};
return component.gcCounter = 0;
};
return component;
};
exports.GroupedInput = exports.WirePattern;
exports.CustomError = function(message, options) {
var err;
err = new Error(message);
return exports.CustomizeError(err, options);
};
exports.CustomizeError = function(err, options) {
var key, val;
for (key in options) {
if (!__hasProp.call(options, key)) continue;
val = options[key];
err[key] = val;
}
return err;
};
exports.MultiError = function(component, group, errorPort, forwardedGroups) {
var baseShutdown;
if (group == null) {
group = '';
}
if (errorPort == null) {
errorPort = 'error';
}
if (forwardedGroups == null) {
forwardedGroups = [];
}
component.hasErrors = false;
component.errors = [];
component.error = function(e, groups) {
if (groups == null) {
groups = [];
}
component.errors.push({
err: e,
groups: forwardedGroups.concat(groups)
});
return component.hasErrors = true;
};
component.fail = function(e, groups) {
var error, grp, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2;
if (e == null) {
e = null;
}
if (groups == null) {
groups = [];
}
if (e) {
component.error(e, groups);
}
if (!component.hasErrors) {
return;
}
if (!(errorPort in component.outPorts)) {
return;
}
if (!component.outPorts[errorPort].isAttached()) {
return;
}
if (group) {
component.outPorts[errorPort].beginGroup(group);
}
_ref = component.errors;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
error = _ref[_i];
_ref1 = error.groups;
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
grp = _ref1[_j];
component.outPorts[errorPort].beginGroup(grp);
}
component.outPorts[errorPort].send(error.err);
_ref2 = error.groups;
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
grp = _ref2[_k];
component.outPorts[errorPort].endGroup();
}
}
if (group) {
component.outPorts[errorPort].endGroup();
}
component.outPorts[errorPort].disconnect();
component.hasErrors = false;
return component.errors = [];
};
baseShutdown = component.shutdown;
component.shutdown = function() {
baseShutdown.call(component);
component.hasErrors = false;
return component.errors = [];
};
return component;
};
}).call(this);