UNPKG

noflo

Version:

Flow-Based Programming environment for JavaScript

763 lines (750 loc) 26.7 kB
(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);