react 0.0.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.npmignore +6 -0
- package/Jakefile.js +8 -0
- package/README.md +226 -86
- package/examples/ast1.js +26 -0
- package/examples/chain-events1.js +34 -0
- package/examples/chain1.js +19 -0
- package/examples/default-events1.js +52 -0
- package/examples/default1.js +40 -0
- package/examples/fstr-events1.js +51 -0
- package/examples/fstr1.js +36 -0
- package/examples/pcode1.js +22 -0
- package/jake-tasks/jake-test.js +64 -0
- package/lib/base-task.js +115 -0
- package/lib/cb-task.js +67 -0
- package/lib/chain.js +148 -0
- package/lib/core.js +96 -0
- package/lib/dsl.js +122 -0
- package/lib/error.js +37 -0
- package/lib/event-manager.js +57 -0
- package/lib/finalcb-first-task.js +59 -0
- package/lib/finalcb-task.js +54 -0
- package/lib/fstr.js +110 -0
- package/lib/id.js +10 -0
- package/lib/input-parser.js +44 -0
- package/lib/parse.js +29 -0
- package/lib/pcode.js +164 -0
- package/lib/ret-task.js +67 -0
- package/lib/status.js +5 -0
- package/lib/task.js +234 -0
- package/lib/validate.js +102 -0
- package/lib/vcon.js +76 -0
- package/oldExamples/analyze.js +29 -0
- package/oldExamples/analyze2.js +29 -0
- package/oldExamples/example10-dsl.js +63 -0
- package/oldExamples/example11.js +62 -0
- package/oldExamples/example12.js +63 -0
- package/oldExamples/example13.js +63 -0
- package/oldExamples/example14.js +63 -0
- package/oldExamples/example15.js +75 -0
- package/{test → oldExamples}/example6-ast.js +4 -4
- package/{test → oldExamples}/example6-dsl.js +3 -2
- package/{test → oldExamples}/example8-ast.js +3 -2
- package/{test → oldExamples}/example8-dsl.js +3 -2
- package/{test → oldExamples}/example9-ast.js +3 -2
- package/{test → oldExamples}/example9-dsl.js +3 -2
- package/oldExamples/function-str-ex1.js +33 -0
- package/oldExamples/function-str-ex2.js +67 -0
- package/oldExamples/trait1.js +41 -0
- package/oldExamples/trait2.js +44 -0
- package/package.json +16 -6
- package/react.js +11 -1
- package/test/ast.test.js +69 -0
- package/test/cb-task.test.js +197 -0
- package/test/chain.test.js +239 -0
- package/test/core.test.js +519 -0
- package/test/dsl.test.js +237 -0
- package/test/event-manager.test.js +102 -0
- package/test/exec-options.test.js +32 -0
- package/test/finalcb-task.test.js +37 -0
- package/test/fstr.test.js +288 -0
- package/test/input-parser.test.js +62 -0
- package/test/module-use.test.js +317 -0
- package/test/pcode.test.js +321 -0
- package/test/ret-task.test.js +199 -0
- package/test/task.test.js +21 -0
- package/test/validate-cb-task.test.js +74 -0
- package/test/validate-ret-task.test.js +83 -0
- package/test/validate.test.js +218 -0
- package/test/vcon.test.js +160 -0
- package/lib/react.js +0 -239
package/lib/chain.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var sprintf = require('sprintf').sprintf;
|
|
4
|
+
var core = require('./core.js');
|
|
5
|
+
var tutil = require('./task.js');
|
|
6
|
+
|
|
7
|
+
// err for task type cb is implied and thus optional, but allow for clarity.
|
|
8
|
+
var ERROR_NAMES_RE = /^err$/i; // first out param matching this is skipped as being the err object
|
|
9
|
+
|
|
10
|
+
// callback is implied for task type cb is implied and thus optional, but allow for clarity.
|
|
11
|
+
var CALLBACK_NAMES_RE = /^cb$|^callback$/i; // last in param matching this is skipped as being the cb
|
|
12
|
+
|
|
13
|
+
var FlowBuilder;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
jQuery-like chain interface for defining flow
|
|
17
|
+
|
|
18
|
+
@example
|
|
19
|
+
// normal flow
|
|
20
|
+
var react = require('react');
|
|
21
|
+
var fn = react.chainDefine()
|
|
22
|
+
.in('filename', 'uid', 'outDirname', 'cb')
|
|
23
|
+
.out('err', 'html', 'user', 'bytesWritten')
|
|
24
|
+
.async(loadUser) .in('uid') .out('err', 'user')
|
|
25
|
+
.async(loadFile) .in('filename') .out('err', 'filedata')
|
|
26
|
+
.sync(markdown) .in('filedata') .out('html')
|
|
27
|
+
.async(prepareDirectory) .in('outDirname') .out('err', 'dircreated')
|
|
28
|
+
.async(writeOutput) .in('html', 'user') .out('err', 'bytesWritten') .after('prepareDirectory')
|
|
29
|
+
.async(loadEmailTemplate) .in() .out('err', 'emailmd')
|
|
30
|
+
.sync(markdown) .in('emailmd') .out('emailHtml')
|
|
31
|
+
.sync(customizeEmail) .in('user', 'emailHtml') .out('custEmailHtml')
|
|
32
|
+
.async(deliverEmail) .in('custEmailHtml') .out('err', 'deliveredEmail') .after('writeOutput')
|
|
33
|
+
.end();
|
|
34
|
+
|
|
35
|
+
@example
|
|
36
|
+
// selectFirst flow
|
|
37
|
+
var fn = chainDefine()
|
|
38
|
+
.selectFirst()
|
|
39
|
+
.in('a', 'b', 'cb')
|
|
40
|
+
.out('err', 'c')
|
|
41
|
+
.async(falpha).in('a', 'b', 'cb').out('err', 'c')
|
|
42
|
+
.sync(fbeta).in('a', 'b').out('c')
|
|
43
|
+
.end();
|
|
44
|
+
*/
|
|
45
|
+
function chainDefine() {
|
|
46
|
+
return FlowBuilder.create();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function FlowBuilder() {
|
|
50
|
+
this.main = {
|
|
51
|
+
name: null,
|
|
52
|
+
options: {}
|
|
53
|
+
};
|
|
54
|
+
this.tasks = [];
|
|
55
|
+
this.focus = this.main;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
FlowBuilder.create = function () { return new FlowBuilder(); };
|
|
59
|
+
|
|
60
|
+
FlowBuilder.prototype.selectFirst = function () {
|
|
61
|
+
this.main.outTaskType = 'finalcbFirst';
|
|
62
|
+
return this;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
FlowBuilder.prototype.name = function (name) {
|
|
66
|
+
this.focus.name = name;
|
|
67
|
+
return this;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
FlowBuilder.prototype.in = function (param1, param2, paramN) {
|
|
71
|
+
var args = Array.prototype.slice.call(arguments);
|
|
72
|
+
if (args.length && this.focus.type !== 'ret') { // has args and cb or main
|
|
73
|
+
if (args[args.length - 1].match(CALLBACK_NAMES_RE)) args.pop(); // pop off the cb name if specified
|
|
74
|
+
}
|
|
75
|
+
if (this.focus === this.main) this.focus.in = args;
|
|
76
|
+
else this.focus.a = args; // for tasks
|
|
77
|
+
return this;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
FlowBuilder.prototype.out = function (param1, param2, paramN) {
|
|
81
|
+
var args = Array.prototype.slice.call(arguments);
|
|
82
|
+
if (args.length && this.focus.type !== 'ret') { // has args and cb or main
|
|
83
|
+
if (args[0].match(ERROR_NAMES_RE)) args.shift(); // shift off err if specified
|
|
84
|
+
}
|
|
85
|
+
this.focus.out = args;
|
|
86
|
+
return this;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
FlowBuilder.prototype.options = function (options) {
|
|
90
|
+
var self = this;
|
|
91
|
+
if (this.focus === this.main) {
|
|
92
|
+
Object.keys(options).forEach(function (k) { self.focus.options[k] = options[k]; });
|
|
93
|
+
} else { // task so set options right on task
|
|
94
|
+
Object.keys(options).forEach(function (k) { self.focus[k] = options[k]; });
|
|
95
|
+
}
|
|
96
|
+
return this;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
FlowBuilder.prototype.async = function (funcOrStrMethod) {
|
|
100
|
+
var task = { f: funcOrStrMethod, type: 'cb' };
|
|
101
|
+
this.tasks.push(task);
|
|
102
|
+
this.focus = task;
|
|
103
|
+
return this;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
FlowBuilder.prototype.sync = function (funcOrStrMethod) {
|
|
107
|
+
var task = { f: funcOrStrMethod, type: 'ret' };
|
|
108
|
+
this.tasks.push(task);
|
|
109
|
+
this.focus = task;
|
|
110
|
+
return this;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
FlowBuilder.prototype.after = function (name1, name2, nameN) {
|
|
114
|
+
this.focus.after = Array.prototype.slice.call(arguments);
|
|
115
|
+
return this;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
Complete the building of a flow and perform validation
|
|
120
|
+
which throws error if flow is not valid.
|
|
121
|
+
*/
|
|
122
|
+
FlowBuilder.prototype.end = function end() {
|
|
123
|
+
var reactFn = core();
|
|
124
|
+
|
|
125
|
+
if (this.main.outTaskType === 'finalcbFirst') {
|
|
126
|
+
this.tasks = tutil.serializeTasks(this.tasks);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
var ast = {
|
|
130
|
+
inParams: this.main.in || [],
|
|
131
|
+
tasks: this.tasks,
|
|
132
|
+
outTask: {
|
|
133
|
+
a: this.main.out || [],
|
|
134
|
+
type: this.main.outTaskType || 'finalcb'
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
if (this.main.name) ast.name = this.main.name;
|
|
138
|
+
var self = this;
|
|
139
|
+
Object.keys(this.main.options).forEach(function (k) { ast[k] = self.main.options[k]; });
|
|
140
|
+
var errors = reactFn.setAndValidateAST(ast);
|
|
141
|
+
if (errors.length) {
|
|
142
|
+
var errorStr = errors.join('\n');
|
|
143
|
+
throw new Error(errorStr);
|
|
144
|
+
}
|
|
145
|
+
return reactFn;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
module.exports = chainDefine;
|
package/lib/core.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var EventEmitter = require('events').EventEmitter;
|
|
4
|
+
|
|
5
|
+
var error = require('./error.js');
|
|
6
|
+
var validate = require('./validate.js');
|
|
7
|
+
var tskutil = require('./task.js');
|
|
8
|
+
var STATUS = require('./status.js');
|
|
9
|
+
var VContext = require('./vcon.js');
|
|
10
|
+
var FinalCbTask = require('./finalcb-task.js');
|
|
11
|
+
var EventManager = require('./event-manager.js');
|
|
12
|
+
var inputParser = require('./input-parser.js');
|
|
13
|
+
var idGenerator = require('./id.js');
|
|
14
|
+
|
|
15
|
+
var reactOptions = {
|
|
16
|
+
stackTraceLimitMin: 30
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
var reactEmitter = EventManager.create(); // the top emitter
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
Creates react function which the AST can be manipulated and then
|
|
23
|
+
is ready to be executed. Can be used directly or a DSL can wrap this
|
|
24
|
+
to provide the AST.
|
|
25
|
+
|
|
26
|
+
@example
|
|
27
|
+
var react = require('react');
|
|
28
|
+
var fn = react();
|
|
29
|
+
var valid2 = fn.setAndValidateAST({
|
|
30
|
+
name: 'optionalName',
|
|
31
|
+
inParams: ['a', 'b'],
|
|
32
|
+
tasks: [
|
|
33
|
+
{ type: 'cb', f: multiply, a: ['a', 'b'], out: ['c'] }
|
|
34
|
+
],
|
|
35
|
+
outTask: { a: ['c'] }
|
|
36
|
+
});
|
|
37
|
+
console.log(fn.ast); // view
|
|
38
|
+
fn(123, 456, cb);
|
|
39
|
+
*/
|
|
40
|
+
function reactFactory() {
|
|
41
|
+
if (arguments.length) throw new Error('react() takes no args, check API');
|
|
42
|
+
|
|
43
|
+
error.ensureStackTraceLimitSet(reactOptions.stackTraceLimitMin);
|
|
44
|
+
var flowEmitter = EventManager.create();
|
|
45
|
+
flowEmitter.parent = reactEmitter;
|
|
46
|
+
|
|
47
|
+
var ast = {
|
|
48
|
+
name: undefined,
|
|
49
|
+
inParams: [],
|
|
50
|
+
tasks: [],
|
|
51
|
+
outTask: {},
|
|
52
|
+
locals: {}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function setAndValidateAST(newAST) { //set AST then validate, ret error[]
|
|
56
|
+
Object.keys(newAST).forEach(function (k) { ast[k] = newAST[k]; }); // copy all properties
|
|
57
|
+
var errors = validate(ast);
|
|
58
|
+
if (!errors.length) tskutil.nameTasks(ast.tasks); //run this so names can be checked in ast
|
|
59
|
+
return errors;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function exec(arg1, arg2, argN, cb) { // called to execute the flow
|
|
63
|
+
var parsedInput = inputParser(Array.prototype.slice.call(arguments), ast);
|
|
64
|
+
var args = parsedInput.args;
|
|
65
|
+
var cbFinal = parsedInput.cb;
|
|
66
|
+
var extraArgs = parsedInput.extraArgs; // we'll have these if we need them
|
|
67
|
+
var vCon = VContext.create(args, ast.inParams, ast.locals); // create var ctx with in args & locals
|
|
68
|
+
var tasks = ast.tasks.map(tskutil.create);
|
|
69
|
+
var tasksByName = tskutil.nameTasks(tasks); // map names to working tasks
|
|
70
|
+
var outTask = tskutil.createOutTask(ast.outTask, cbFinal, tasks, vCon);
|
|
71
|
+
var handleError = tskutil.createErrorHandler(vCon, outTask);
|
|
72
|
+
|
|
73
|
+
function contExec() {
|
|
74
|
+
if (!outTask.f) { return; } //stop execution, we already hit an error, f was cleared
|
|
75
|
+
if (outTask.isReady()) return outTask.exec(); // all tasks done, exec cb, return
|
|
76
|
+
tskutil.findReadyAndExec(vCon, tasks, tasksByName, handleError, contExec); //exec tasks that ready to run
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
tasks.forEach(function (t) {
|
|
80
|
+
t.id = idGenerator.createUniqueId();
|
|
81
|
+
t.flowEmitter = flowEmitter;
|
|
82
|
+
if (t.type === 'cb') t.cbFun = tskutil.createCallback(t, handleError, vCon, contExec, flowEmitter);
|
|
83
|
+
}); // create callbacks
|
|
84
|
+
contExec(); // start things off
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
var reactFn = exec; // make the exec() the function returned
|
|
88
|
+
reactFn.ast = ast; // put AST hanging off the fn so it can be inspected
|
|
89
|
+
reactFn.setAndValidateAST = setAndValidateAST; // call to set AST and then validate
|
|
90
|
+
reactFn.events = flowEmitter; // used to listen to execution events for this flow
|
|
91
|
+
return reactFn;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = reactFactory; // module returns reactFactory to create a react fn
|
|
95
|
+
module.exports.options = reactOptions; // global react options
|
|
96
|
+
module.exports.events = reactEmitter; // global react emitter
|
package/lib/dsl.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/*jshint regexp: false */
|
|
3
|
+
|
|
4
|
+
var sprintf = require('sprintf').sprintf;
|
|
5
|
+
var core = require('./core.js');
|
|
6
|
+
var parse = require('./parse.js');
|
|
7
|
+
var tutil = require('./task.js');
|
|
8
|
+
|
|
9
|
+
var INOUT_PARAMS_NO_MATCH = 'params in wrong format, wanted "foo, bar cb -> err, baz" - found: %s';
|
|
10
|
+
var EXTRA_TASKARG = 'extra unmatched task arg: %s';
|
|
11
|
+
|
|
12
|
+
var CB_NAMES_RE = /^cb|callback$/i; //cb, Cb, CB, callback, Callback
|
|
13
|
+
var ERR_NAMES_RE = /^err$/i; // err, ERR, Err, ...
|
|
14
|
+
|
|
15
|
+
function filterOutTrailingCbParam(args) { // if has trailing cb | callback param, filter it out
|
|
16
|
+
if (args.length && args[args.length - 1].match(CB_NAMES_RE)) args.pop();
|
|
17
|
+
return args;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function filterOutLeadingErrParam(args) { // if leading err param, filter it out
|
|
21
|
+
if (args.length && args[0].match(ERR_NAMES_RE)) args.shift();
|
|
22
|
+
return args;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var inOutDefParse = {
|
|
26
|
+
regex: /^([^\-]*)(->\s*(.*))?$/i,
|
|
27
|
+
fn: function (m) {
|
|
28
|
+
var inParams = parse.splitTrimFilterArgs(m[1]);
|
|
29
|
+
var lastParam = inParams[inParams.length - 1];
|
|
30
|
+
var type = (lastParam && CB_NAMES_RE.test(lastParam)) ? 'cb' : 'ret';
|
|
31
|
+
var outParams = parse.splitTrimFilterArgs(m[3]);
|
|
32
|
+
return {
|
|
33
|
+
type: type,
|
|
34
|
+
inDef: filterOutTrailingCbParam(inParams),
|
|
35
|
+
outDef: filterOutLeadingErrParam(outParams)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
function parseInOutParams(str) {
|
|
41
|
+
var objDef = parse.parseStr(str, [inOutDefParse], INOUT_PARAMS_NO_MATCH);
|
|
42
|
+
objDef.inDef = filterOutTrailingCbParam(objDef.inDef);
|
|
43
|
+
return objDef;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function parseTasks(arr) {
|
|
47
|
+
var tasks = [];
|
|
48
|
+
var fn, obj, result;
|
|
49
|
+
while (arr.length >= 2) {
|
|
50
|
+
obj = {};
|
|
51
|
+
fn = arr.shift();
|
|
52
|
+
result = parseInOutParams(arr.shift());
|
|
53
|
+
if (typeof(arr[0]) === 'object') obj = arr.shift(); // has options, use as obj
|
|
54
|
+
obj.f = fn;
|
|
55
|
+
obj.a = result.inDef;
|
|
56
|
+
var type = result.type;
|
|
57
|
+
obj.out = result.outDef;
|
|
58
|
+
obj.type = type;
|
|
59
|
+
tasks.push(obj);
|
|
60
|
+
}
|
|
61
|
+
if (arr.length) throw new Error(sprintf(EXTRA_TASKARG, arr[0]));
|
|
62
|
+
return tasks;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
Parse the variable arguments into in/out params, options, tasks
|
|
67
|
+
*/
|
|
68
|
+
function parseVargs(vargs) {
|
|
69
|
+
var inOutParamStr = vargs.shift() || '';
|
|
70
|
+
// if next arg is object, shift it off as options
|
|
71
|
+
var options = (vargs.length && typeof(vargs[0]) === 'object') ? vargs.shift() : { };
|
|
72
|
+
var taskDefArr = vargs; // rest are for the tasks
|
|
73
|
+
var defObj = {
|
|
74
|
+
inOutParamStr: inOutParamStr,
|
|
75
|
+
taskDefArr: taskDefArr,
|
|
76
|
+
options: options
|
|
77
|
+
};
|
|
78
|
+
return defObj;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
function dslDefine(name, arg1, arg2, argN) {
|
|
83
|
+
var reactFn = core();
|
|
84
|
+
var defObj = parseVargs(Array.prototype.slice.call(arguments, 1)); // name, already used
|
|
85
|
+
var inOutDef = parseInOutParams(defObj.inOutParamStr);
|
|
86
|
+
var ast = {
|
|
87
|
+
name: name,
|
|
88
|
+
inParams: inOutDef.inDef,
|
|
89
|
+
tasks: parseTasks(defObj.taskDefArr),
|
|
90
|
+
outTask: { a: inOutDef.outDef }
|
|
91
|
+
};
|
|
92
|
+
if (defObj.options) Object.keys(defObj.options).forEach(function (k) { ast[k] = defObj.options[k]; });
|
|
93
|
+
var errors = reactFn.setAndValidateAST(ast);
|
|
94
|
+
if (errors.length) {
|
|
95
|
+
var errorStr = errors.join('\n');
|
|
96
|
+
throw new Error(errorStr);
|
|
97
|
+
}
|
|
98
|
+
return reactFn;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function selectFirst(name, arg1, arg2, argN) {
|
|
102
|
+
var reactFn = core();
|
|
103
|
+
var defObj = parseVargs(Array.prototype.slice.call(arguments, 1)); // name, already used
|
|
104
|
+
var inOutDef = parseInOutParams(defObj.inOutParamStr);
|
|
105
|
+
var tasks = tutil.serializeTasks(parseTasks(defObj.taskDefArr));
|
|
106
|
+
var ast = {
|
|
107
|
+
name: name,
|
|
108
|
+
inParams: inOutDef.inDef,
|
|
109
|
+
tasks: tasks,
|
|
110
|
+
outTask: { type: 'finalcbFirst', a: inOutDef.outDef },
|
|
111
|
+
};
|
|
112
|
+
if (defObj.options) Object.keys(defObj.options).forEach(function (k) { ast[k] = defObj.options[k]; });
|
|
113
|
+
var errors = reactFn.setAndValidateAST(ast);
|
|
114
|
+
if (errors.length) {
|
|
115
|
+
var errorStr = errors.join('\n');
|
|
116
|
+
throw new Error(errorStr);
|
|
117
|
+
}
|
|
118
|
+
return reactFn;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = dslDefine;
|
|
122
|
+
module.exports.selectFirst = selectFirst;
|
package/lib/error.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var util = require('util');
|
|
4
|
+
|
|
5
|
+
function ensureStackTraceLimitSet(stackTraceLimit) {
|
|
6
|
+
if (!Error.stackTraceLimit || Error.stackTraceLimit < stackTraceLimit) {
|
|
7
|
+
Error.stackTraceLimit = stackTraceLimit;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function fName(fn) {
|
|
12
|
+
return (typeof(fn) === 'string') ? fn : fn.name;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function formatErrorMeta(err) {
|
|
16
|
+
if (!err.meta) return;
|
|
17
|
+
var vcon = err.meta.vcon;
|
|
18
|
+
var task = err.meta.task;
|
|
19
|
+
return '\n\n' +
|
|
20
|
+
'Error occurs in Task function: ' + fName(task.f) + '(' + task.a.join(',') + ')\n\n' +
|
|
21
|
+
'Variable Context: \n' +
|
|
22
|
+
util.inspect(vcon) + '\n\n' +
|
|
23
|
+
'Task Source:\n\n' +
|
|
24
|
+
task.f.toString() + '\n\n'; //TODO need to pretty print function, gets collapsed
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function augmentError(err, meta) {
|
|
28
|
+
if (typeof(err) === 'string') { err = new Error(err); } //props will be lost on non-objects
|
|
29
|
+
var origMsg = err.toString();
|
|
30
|
+
err.meta = meta;
|
|
31
|
+
err.toString = function () { return origMsg + formatErrorMeta(err); };
|
|
32
|
+
return err;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
exports.ensureStackTraceLimitSet = ensureStackTraceLimitSet;
|
|
37
|
+
exports.augmentError = augmentError;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var EventEmitter2 = require('eventemitter2').EventEmitter2;
|
|
4
|
+
|
|
5
|
+
var EVENT_EMITTER2_CONFIG = {
|
|
6
|
+
wildcard: true
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
var TYPES = {
|
|
10
|
+
TASK_BEGIN: 'task.begin',
|
|
11
|
+
TASK_COMPLETE: 'task.complete'
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
Event manager which emits events up to its parent if exists.
|
|
16
|
+
Allows a hierarchy of emitters, which communicate up the
|
|
17
|
+
chain.
|
|
18
|
+
*/
|
|
19
|
+
function EventManager() {
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
EventManager.create = function () { return new EventManager(); };
|
|
23
|
+
|
|
24
|
+
EventManager.prototype.isEnabled = function () { // if has listener or an ancestor has listener
|
|
25
|
+
return (this.emitter || (this.parent && this.parent.isEnabled()));
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
Add listener. Wildcard events are allowed like 'foo.*'
|
|
30
|
+
Use '*' to listen to any event
|
|
31
|
+
*/
|
|
32
|
+
EventManager.prototype.on = function (event, listener) {
|
|
33
|
+
if (!this.emitter) this.emitter = new EventEmitter2(EVENT_EMITTER2_CONFIG);
|
|
34
|
+
if (event === '*') this.emitter.onAny(listener);
|
|
35
|
+
else this.emitter.on(event, listener);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
EventManager.prototype.emit = function (event, arg1, arg2, argN) {
|
|
39
|
+
if (this.emitter) this.emitter.emit.apply(this.emitter, arguments);
|
|
40
|
+
if (this.parent && this.parent.isEnabled()) this.parent.emit.apply(this.parent, arguments);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
Emit an object augmented with standard fields.
|
|
45
|
+
Copies object and adds standard fields:
|
|
46
|
+
event: event type
|
|
47
|
+
time: current time
|
|
48
|
+
*/
|
|
49
|
+
EventManager.prototype.emitObject = function (event, object) {
|
|
50
|
+
var evObj = Object.create(object); // create inherited copy version so origin is untouched
|
|
51
|
+
evObj.event = event; // augment with the event type
|
|
52
|
+
evObj.time = Date.now(); // augument with the time of the event
|
|
53
|
+
this.emit(event, evObj);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
module.exports = EventManager;
|
|
57
|
+
module.exports.TYPES = TYPES;
|