total.js
Version:
MVC framework for Node.js
491 lines (403 loc) • 11.6 kB
JavaScript
// Copyright 2012-2020 (c) Peter Širka <petersirka@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
/**
* @module FrameworkFlowStream
* @version 3.4.0
*/
if (!global.framework_utils)
global.framework_utils = require('./utils');
const D = '__';
function Message() {}
Message.prototype = {
get user() {
return this.controller ? this.controller.user : null;
},
get session() {
return this.controller ? this.controller.session : null;
},
get sessionid() {
return this.controller && this.controller ? this.controller.req.sessionid : null;
},
get language() {
return (this.controller ? this.controller.language : '') || '';
},
get ip() {
return this.controller ? this.controller.ip : null;
},
get id() {
return this.controller ? this.controller.id : null;
},
get req() {
return this.controller ? this.controller.req : null;
},
get res() {
return this.controller ? this.controller.res : null;
},
get params() {
return this.controller ? this.controller.params : null;
},
get files() {
return this.controller ? this.controller.files : null;
},
get body() {
return this.controller ? this.controller.body : null;
},
get query() {
return this.controller ? this.controller.query : null;
},
get headers() {
return this.controller && this.controller.req ? this.controller.req.headers : null;
},
get ua() {
return this.controller && this.controller.req ? this.controller.req.ua : null;
}
};
var MP = Message.prototype;
MP.emit = function(name, a, b, c, d, e, f, g) {
var self = this;
if (!self.$events)
return self;
var evt = self.$events[name];
if (evt) {
var clean = false;
for (var i = 0, length = evt.length; i < length; i++) {
if (evt[i].$once)
clean = true;
evt[i].call(self, a, b, c, d, e, f, g);
}
if (clean) {
evt = evt.remove(n => n.$once);
self.$events[name] = evt.length ? evt : undefined;
}
}
return self;
};
MP.on = function(name, fn) {
var self = this;
if (!self.$events)
self.$events = {};
if (self.$events[name])
self.$events[name].push(fn);
else
self.$events[name] = [fn];
return self;
};
MP.once = function(name, fn) {
fn.$once = true;
return this.on(name, fn);
};
MP.removeListener = function(name, fn) {
var self = this;
if (self.$events) {
var evt = self.$events[name];
if (evt) {
evt = evt.remove(n => n === fn);
self.$events[name] = evt.length ? evt : undefined;
}
}
return self;
};
MP.removeAllListeners = function(name) {
if (this.$events) {
if (name === true)
this.$events = {};
else if (name)
this.$events[name] = undefined;
else
this.$events = {};
}
return this;
};
MP.clone = function() {
var self = this;
var obj = new Message();
obj.$events = self.$events;
obj.duration = self.duration;
obj.repo = self.repo;
obj.main = self.main;
obj.count = self.count;
obj.data = self.data;
obj.used = self.used;
obj.processed = 0;
return obj;
};
MP.send = function(outputindex) {
var self = this;
var outputs;
var count = 0;
if (outputindex == null) {
if (self.schema.connections) {
outputs = Object.keys(self.schema.connections);
for (var i = 0; i < outputs.length; i++)
count += self.send(outputs[i]);
}
return count;
}
var meta = self.main.meta;
var now = Date.now();
outputs = self.schema.connections ? (self.schema.connections[outputindex] || EMPTYARRAY) : EMPTYARRAY;
if (self.processed === 0) {
self.processed = 1;
self.schema.stats.pending--;
self.schema.stats.output++;
self.schema.stats.duration = now - self.duration2;
}
if (!self.main.$can(false, self.toid, outputindex))
return count;
for (var i = 0; i < outputs.length; i++) {
var output = outputs[i];
if (output.disabled || output.paused)
continue;
var schema = meta.flow[output.id];
if (schema && schema.component && self.main.$can(true, output.id, output.index)) {
var next = meta.components[schema.component];
if (next && next.message) {
var inputindex = output.index;
var message = self.clone();
message.used++;
message.from = self.to;
message.fromid = self.toid;
message.fromindex = outputindex;
message.fromcomponent = self.schema.component;
message.fromschema = self.toschema;
message.to = next;
message.toid = output.id;
message.toindex = inputindex;
message.tocomponent = schema.component;
message.toschema = message.schema = schema;
message.cache = schema.cache;
message.options = schema.options;
message.duration2 = now;
schema.stats.input++;
schema.stats.pending++;
self.$events.message && self.emit('message', message);
self.main.$events.message && self.main.emit('message', message);
setImmediate(sendmessage, next, message);
count++;
}
}
}
return count;
};
MP.replace = function(data) {
this.data = data;
return this;
};
MP.destroy = function() {
var self = this;
if (self.processed === 0) {
self.processed = 1;
self.schema.stats.pending--;
self.schema.stats.output++;
self.schema.stats.duration = Date.now() - self.duration2;
}
self.$events.end && self.emit('end', self);
self.main.$events.end && self.main.emit('end', self);
self.repo = null;
self.main = null;
self.from = null;
self.to = null;
self.fromschema = null;
self.toschema = null;
self.data = null;
self.options = null;
self.duration = null;
self.duration2 = null;
self.$events = null;
};
function Flow(name) {
var t = this;
t.name = name;
t.meta = {};
t.meta.components = {};
t.meta.messages = 0;
t.meta.flow = {};
t.meta.cache = {};
t.$events = {};
new framework_utils.EventEmitter2(t);
}
var FP = Flow.prototype;
FP.register = function(name, declaration) {
var self = this;
if (typeof(declaration) === 'string')
declaration = new Function('instance', declaration);
var cache;
var prev = self.meta.components[name];
if (prev) {
cache = prev.cache;
prev.connected = false;
prev.disabled = true;
prev.destroy = null;
prev.disconnect && prev.disconnect();
}
var curr = { id: name, main: self, connected: true, disabled: false, cache: cache || {} };
declaration(curr);
self.meta.components[name] = curr;
self.$events.register && self.emit('register', name, curr);
curr.install && !prev && curr.install();
curr.connect && curr.connect();
curr.destroy = function() {
self.unregister(name);
};
return self;
};
FP.destroy = function() {
var self = this;
self.unregister();
setTimeout(function() {
self.emit('destroy');
self.meta = null;
self.$events = null;
}, 500);
delete F.flows[self.name];
};
FP.unregister = function(name) {
var self = this;
if (name == null) {
var keys = Object.keys(self.meta.components);
for (var i = 0; i < keys.length; i++)
self.unregister(self.meta.components[keys[i]]);
return self;
}
var curr = self.meta.components[name];
if (curr) {
self.$events.unregister && self.emit('unregister', name, curr);
curr.connected = false;
curr.disabled = true;
curr.destroy = null;
curr.cache = null;
curr.disconnect && curr.disconnect();
curr.uninstall && curr.uninstall();
delete self.meta.components[name];
}
return self;
};
FP.use = function(schema, callback) {
var self = this;
if (typeof(schema) === 'string')
schema = schema.parseJSON(true);
// schema.COMPONENT_ID.component = 'condition';
// schema.COMPONENT_ID.options = {};
// schema.COMPONENT_ID.connections = { '0': [{ id: 'COMPONENT_ID', index: '2' }] }
var err = new ErrorBuilder();
if (schema) {
var keys = Object.keys(schema);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (key === 'paused')
continue;
var instance = schema[key];
if (!instance.component)
continue;
var component = self.meta.components[instance.component];
schema[key].stats = { pending: 0, input: 0, output: 0, duration: 0 };
schema[key].cache = {};
if (!component)
err.push(key, '"' + instance.component + '" component not found.');
component.options && component.options.call(schema[key], schema[key].options);
}
self.meta.flow = schema;
} else
err.push('schema', 'Flow schema is invalid.');
self.$events.schema && self.emit('schema', schema);
callback && callback(err.length ? err : null);
return self;
};
function sendmessage(instance, message, event) {
if (event) {
message.$events.message && message.emit('message', message);
message.main.$events.message && message.main.emit('message', message);
}
instance.message(message);
}
FP.$can = function(isinput, id, index) {
var self = this;
if (!self.meta.flow.paused)
return true;
var key = (isinput ? 'input' : 'output') + D + id + D + index;
if (!self.meta.flow.paused[key])
return true;
};
// path = ID__INPUTINDEX
FP.trigger = function(path, data, controller, events) {
path = path.split(D);
var self = this;
var inputindex = path.length === 1 ? 0 : path[1];
var schema = self.meta.flow[path[0]];
if (schema && schema.component) {
var instance = self.meta.components[schema.component];
if (instance && instance.message && self.$can(true, path[0], path[1])) {
var message = new Message();
message.$events = events || {};
message.duration = message.duration2 = Date.now();
message.controller = controller;
message.used = 1;
message.repo = {};
message.main = self;
message.data = data;
message.count = self.meta.messages++;
message.from = null;
message.fromid = null;
message.fromindex = null;
message.fromcomponent = null;
message.fromschema = null;
message.to = instance;
message.toid = path[0];
message.toindex = inputindex;
message.tocomponent = instance.id;
message.toschema = message.schema = schema;
message.cache = instance.cache;
message.options = schema.options;
message.processed = 0;
schema.stats.input++;
schema.stats.pending++;
setImmediate(sendmessage, instance, message, true);
return message;
}
}
};
FP.trigger2 = function(path, data, controller) {
var self = this;
var keys = Object.keys(self.meta.flow);
var events = {};
var obj;
path = path.split(D);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var flow = self.meta.flow[key];
if (flow.component === path[0])
obj = self.trigger(key + D + (path.length === 1 ? 0 : path[1]), data, controller, events);
}
return obj;
};
FP.clear = function() {
var self = this;
self.meta.flow = {};
return self;
};
FP.make = function(fn) {
var self = this;
fn.call(self, self);
return self;
};
exports.make = function(name) {
return new Flow(name);
};