react 0.0.3 → 0.1.2

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.
Files changed (66) hide show
  1. package/.npmignore +5 -0
  2. package/Jakefile.js +8 -0
  3. package/README.md +183 -83
  4. package/examples/ast1.js +26 -0
  5. package/examples/chain-events1.js +34 -0
  6. package/examples/chain1.js +19 -0
  7. package/examples/fstr-events1.js +51 -0
  8. package/examples/fstr1.js +36 -0
  9. package/examples/pcode1.js +22 -0
  10. package/jake-tasks/jake-test.js +64 -0
  11. package/lib/base-task.js +115 -0
  12. package/lib/cb-task.js +67 -0
  13. package/lib/chain.js +148 -0
  14. package/lib/core.js +95 -0
  15. package/lib/error.js +37 -0
  16. package/lib/event-manager.js +57 -0
  17. package/lib/finalcb-first-task.js +59 -0
  18. package/lib/finalcb-task.js +54 -0
  19. package/lib/fstr.js +110 -0
  20. package/lib/id.js +10 -0
  21. package/lib/input-parser.js +44 -0
  22. package/lib/parse.js +28 -0
  23. package/lib/pcode.js +164 -0
  24. package/lib/ret-task.js +67 -0
  25. package/lib/status.js +5 -0
  26. package/lib/task.js +234 -0
  27. package/lib/validate.js +102 -0
  28. package/lib/vcon.js +76 -0
  29. package/oldExamples/analyze.js +29 -0
  30. package/oldExamples/analyze2.js +29 -0
  31. package/oldExamples/example10-dsl.js +63 -0
  32. package/oldExamples/example11.js +62 -0
  33. package/oldExamples/example12.js +63 -0
  34. package/oldExamples/example13.js +63 -0
  35. package/oldExamples/example14.js +63 -0
  36. package/oldExamples/example15.js +75 -0
  37. package/{test → oldExamples}/example6-ast.js +0 -0
  38. package/{test → oldExamples}/example6-dsl.js +0 -0
  39. package/{test → oldExamples}/example8-ast.js +0 -0
  40. package/{test → oldExamples}/example8-dsl.js +0 -0
  41. package/{test → oldExamples}/example9-ast.js +0 -0
  42. package/{test → oldExamples}/example9-dsl.js +0 -0
  43. package/oldExamples/function-str-ex1.js +33 -0
  44. package/oldExamples/function-str-ex2.js +67 -0
  45. package/oldExamples/trait1.js +41 -0
  46. package/oldExamples/trait2.js +44 -0
  47. package/package.json +16 -6
  48. package/react.js +11 -1
  49. package/test/ast.test.js +69 -0
  50. package/test/cb-task.test.js +197 -0
  51. package/test/chain.test.js +239 -0
  52. package/test/core.test.js +519 -0
  53. package/test/event-manager.test.js +102 -0
  54. package/test/exec-options.test.js +32 -0
  55. package/test/finalcb-task.test.js +37 -0
  56. package/test/fstr.test.js +288 -0
  57. package/test/input-parser.test.js +62 -0
  58. package/test/module-use.test.js +271 -0
  59. package/test/pcode.test.js +321 -0
  60. package/test/ret-task.test.js +199 -0
  61. package/test/task.test.js +21 -0
  62. package/test/validate-cb-task.test.js +74 -0
  63. package/test/validate-ret-task.test.js +83 -0
  64. package/test/validate.test.js +218 -0
  65. package/test/vcon.test.js +160 -0
  66. package/lib/react.js +0 -254
package/lib/core.js ADDED
@@ -0,0 +1,95 @@
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
+ inParams: [],
49
+ tasks: [],
50
+ outTask: {},
51
+ locals: {}
52
+ };
53
+
54
+ function setAndValidateAST(newAST) { //set AST then validate, ret error[]
55
+ Object.keys(newAST).forEach(function (k) { ast[k] = newAST[k]; }); // copy all properties
56
+ var errors = validate(ast);
57
+ if (!errors.length) tskutil.nameTasks(ast.tasks); //run this so names can be checked in ast
58
+ return errors;
59
+ }
60
+
61
+ function exec(arg1, arg2, argN, cb) { // called to execute the flow
62
+ var parsedInput = inputParser(Array.prototype.slice.call(arguments), ast);
63
+ var args = parsedInput.args;
64
+ var cbFinal = parsedInput.cb;
65
+ var extraArgs = parsedInput.extraArgs; // we'll have these if we need them
66
+ var vCon = VContext.create(args, ast.inParams, ast.locals); // create var ctx with in args & locals
67
+ var tasks = ast.tasks.map(tskutil.create);
68
+ var tasksByName = tskutil.nameTasks(tasks); // map names to working tasks
69
+ var outTask = tskutil.createOutTask(ast.outTask, cbFinal, tasks, vCon);
70
+ var handleError = tskutil.createErrorHandler(vCon, outTask);
71
+
72
+ function contExec() {
73
+ if (!outTask.f) { return; } //stop execution, we already hit an error, f was cleared
74
+ if (outTask.isReady()) return outTask.exec(); // all tasks done, exec cb, return
75
+ tskutil.findReadyAndExec(vCon, tasks, tasksByName, handleError, contExec); //exec tasks that ready to run
76
+ }
77
+
78
+ tasks.forEach(function (t) {
79
+ t.id = idGenerator.createUniqueId();
80
+ t.flowEmitter = flowEmitter;
81
+ if (t.type === 'cb') t.cbFun = tskutil.createCallback(t, handleError, vCon, contExec, flowEmitter);
82
+ }); // create callbacks
83
+ contExec(); // start things off
84
+ }
85
+
86
+ var reactFn = exec; // make the exec() the function returned
87
+ reactFn.ast = ast; // put AST hanging off the fn so it can be inspected
88
+ reactFn.setAndValidateAST = setAndValidateAST; // call to set AST and then validate
89
+ reactFn.events = flowEmitter; // used to listen to execution events for this flow
90
+ return reactFn;
91
+ }
92
+
93
+ module.exports = reactFactory; // module returns reactFactory to create a react fn
94
+ module.exports.options = reactOptions; // global react options
95
+ module.exports.events = reactEmitter; // global react emitter
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;
@@ -0,0 +1,59 @@
1
+ 'use strict';
2
+
3
+ var sprintf = require('sprintf').sprintf;
4
+ var util = require('util');
5
+
6
+ var STATUS = require('./status.js');
7
+ var VContext = require('./vcon.js');
8
+
9
+ var OUTTASK_A_REQ = 'ast.outTask.a should be an array of string param names';
10
+
11
+ function FinalCbFirstSuccTask(taskDef, cbFunc, tasks, vCon) {
12
+ var self = this;
13
+ Object.keys(taskDef).forEach(function (k) { self[k] = taskDef[k]; });
14
+ this.f = cbFunc;
15
+ this.tasks = tasks;
16
+ this.vCon = vCon;
17
+ }
18
+
19
+ function format_error(errmsg, obj) {
20
+ return sprintf('%s - %s', errmsg, util.inspect(obj));
21
+ }
22
+
23
+ FinalCbFirstSuccTask.validate = function (taskDef) {
24
+ var errors = [];
25
+ if (! (Array.isArray(taskDef.a) &&
26
+ taskDef.a.every(function (x) { return (typeof(x) === 'string'); }))) {
27
+ errors.push(format_error(OUTTASK_A_REQ, taskDef));
28
+ }
29
+ return errors;
30
+ }
31
+
32
+ FinalCbFirstSuccTask.create = function (taskDef, cbFunc, tasks, vCon) {
33
+ if (!(cbFunc && cbFunc instanceof Function)) throw new Error('callback is not a function');
34
+ return new FinalCbFirstSuccTask(taskDef, cbFunc, tasks, vCon);
35
+ };
36
+
37
+ /**
38
+ is ready to exit when any task comes back with non-null defined value
39
+ */
40
+ FinalCbFirstSuccTask.prototype.isReady = function () {
41
+ var lastres = this.vCon.getLastResults();
42
+ if (!lastres) return false; // no results yet
43
+ return (lastres.some(function (v) { return (v !== undefined && v !== null); }));
44
+ };
45
+
46
+ FinalCbFirstSuccTask.prototype.exec = function (err) {
47
+ if (!this.f) return; //must have already been called
48
+ if (err) {
49
+ this.f.call(null, err); //call the final callback with the first error hit
50
+ } else { // no error, call with args
51
+ var vCon = this.vCon;
52
+ var finalArgs = this.a.map(function (k) { return vCon.getVar(k); });
53
+ finalArgs.unshift(null); //unshift err=null to front
54
+ this.f.apply(null, finalArgs);
55
+ }
56
+ this.f = null; // prevent multiple calls
57
+ };
58
+
59
+ module.exports = FinalCbFirstSuccTask;
@@ -0,0 +1,54 @@
1
+ 'use strict';
2
+
3
+ var sprintf = require('sprintf').sprintf;
4
+ var util = require('util');
5
+
6
+ var STATUS = require('./status.js');
7
+
8
+ var OUTTASK_A_REQ = 'ast.outTask.a should be an array of string param names';
9
+
10
+ function FinalCbTask(taskDef, cbFunc, tasks, vCon) {
11
+ var self = this;
12
+ Object.keys(taskDef).forEach(function (k) { self[k] = taskDef[k]; });
13
+ this.f = cbFunc;
14
+ this.tasks = tasks;
15
+ this.vCon = vCon;
16
+ }
17
+
18
+ function format_error(errmsg, obj) {
19
+ return sprintf('%s - %s', errmsg, util.inspect(obj));
20
+ }
21
+
22
+
23
+ FinalCbTask.validate = function (taskDef) {
24
+ var errors = [];
25
+ if (! (Array.isArray(taskDef.a) &&
26
+ taskDef.a.every(function (x) { return (typeof(x) === 'string'); }))) {
27
+ errors.push(format_error(OUTTASK_A_REQ, taskDef));
28
+ }
29
+ return errors;
30
+ };
31
+
32
+ FinalCbTask.create = function (taskDef, cbFunc, tasks, vCon) {
33
+ if (!(cbFunc && cbFunc instanceof Function)) throw new Error('callback is not a function');
34
+ return new FinalCbTask(taskDef, cbFunc, tasks, vCon);
35
+ };
36
+
37
+ FinalCbTask.prototype.isReady = function () {
38
+ return (this.tasks.every(function (t) { return (t.status === STATUS.COMPLETE); }));
39
+ };
40
+
41
+ FinalCbTask.prototype.exec = function (err) {
42
+ if (!this.f) return; //must have already been called
43
+ if (err) {
44
+ this.f.call(null, err); //call the final callback with the first error hit
45
+ } else { // no error, call with args
46
+ var vCon = this.vCon;
47
+ var finalArgs = this.a.map(function (k) { return vCon.getVar(k); });
48
+ finalArgs.unshift(null); //unshift err=null to front
49
+ this.f.apply(null, finalArgs);
50
+ }
51
+ this.f = null; // prevent multiple calls
52
+ };
53
+
54
+ module.exports = FinalCbTask;
package/lib/fstr.js ADDED
@@ -0,0 +1,110 @@
1
+ 'use strict';
2
+
3
+ var sprintf = require('sprintf').sprintf;
4
+ var core = require('./core.js');
5
+ var parse = require('./parse.js');
6
+ var tutil = require('./task.js');
7
+
8
+ var INPARAMS_NO_MATCH = 'input params in wrong format, wanted "foo, bar" - found: %s';
9
+ var OUTPARAMS_NO_MATCH = 'output params in wrong format, wanted "foo, bar" - found: %s';
10
+ var INOUT_PARAMS_NO_MATCH = 'task params in wrong format, wanted "foo, bar -> err, baz" - found: %s';
11
+ var EXTRA_TASKARG = 'extra unmatched task arg: %s';
12
+
13
+ var inOutDefParse = {
14
+ regex: /^([^-]*)(->)?\s*(er{0,2}\s*,|returns?\s+)?(.*)$/i,
15
+ fn: function (m) {
16
+ var reMatchReturns = /returns?/i;
17
+ return {
18
+ type: (m[3] && m[3].match(reMatchReturns)) ? 'ret' : 'cb',
19
+ inDef: parse.splitTrimFilterArgs(m[1]),
20
+ outDef: parse.splitTrimFilterArgs(m[4])
21
+ };
22
+ }
23
+ };
24
+
25
+ function filterOutTrailingCbParam(args) { // if has trailing cb | callback param, filter it out
26
+ var cbNamesRe = /^cb|callback$/i; //cb, Cb, CB, callback, Callback
27
+ if (args.length && args[args.length - 1].match(cbNamesRe)) args.pop();
28
+ return args;
29
+ }
30
+
31
+ function parseInParams(str) {
32
+ var objDef = parse.parseStr(str, [inOutDefParse], INPARAMS_NO_MATCH);
33
+ objDef.inDef = filterOutTrailingCbParam(objDef.inDef);
34
+ return objDef;
35
+ }
36
+
37
+ function parseInOutParams(str) {
38
+ var objDef = parse.parseStr(str, [inOutDefParse], INOUT_PARAMS_NO_MATCH);
39
+ objDef.inDef = filterOutTrailingCbParam(objDef.inDef);
40
+ return objDef;
41
+ }
42
+
43
+ function parseOutParams(str) {
44
+ if (str.indexOf('->') === -1) str = '-> ' + str; // prefix so does out err process
45
+ return parse.parseStr(str, [inOutDefParse], OUTPARAMS_NO_MATCH);
46
+ }
47
+
48
+ function parseTasks(arr) {
49
+ var tasks = [];
50
+ var fn, obj, result;
51
+ while (arr.length >= 2) {
52
+ obj = {};
53
+ fn = arr.shift();
54
+ result = parseInOutParams(arr.shift());
55
+ if (typeof(arr[0]) === 'object') obj = arr.shift(); // has options, use as obj
56
+ obj.f = fn;
57
+ obj.a = result.inDef;
58
+ var type = result.type;
59
+ obj.out = result.outDef;
60
+ obj.type = type;
61
+ tasks.push(obj);
62
+ }
63
+ if (arr.length) throw new Error(sprintf(EXTRA_TASKARG, arr[0]));
64
+ return tasks;
65
+ }
66
+
67
+
68
+ function fstrDefine(inParamStr, taskDefArr, outParamStr, options) {
69
+ if (!inParamStr) inParamStr = '';
70
+ if (!taskDefArr) taskDefArr = [];
71
+ if (!outParamStr) outParamStr = '';
72
+
73
+ var reactFn = core();
74
+ var ast = {
75
+ inParams: parseInParams(inParamStr).inDef,
76
+ tasks: parseTasks(taskDefArr),
77
+ outTask: { a: parseOutParams(outParamStr).outDef }
78
+ };
79
+ if (options) Object.keys(options).forEach(function (k) { ast[k] = options[k]; });
80
+ var errors = reactFn.setAndValidateAST(ast);
81
+ if (errors.length) {
82
+ var errorStr = errors.join('\n');
83
+ throw new Error(errorStr);
84
+ }
85
+ return reactFn;
86
+ }
87
+
88
+ function selectFirst(inParamStr, taskDefArr, outParamStr, options) {
89
+ if (!inParamStr) inParamStr = '';
90
+ if (!taskDefArr) taskDefArr = [];
91
+ if (!outParamStr) outParamStr = '';
92
+ var tasks = tutil.serializeTasks(parseTasks(taskDefArr));
93
+
94
+ var reactFn = core();
95
+ var ast = {
96
+ inParams: parseInParams(inParamStr).inDef,
97
+ tasks: tasks,
98
+ outTask: { type: 'finalcbFirst', a: parseOutParams(outParamStr).outDef },
99
+ };
100
+ if (options) Object.keys(options).forEach(function (k) { ast[k] = options[k]; });
101
+ var errors = reactFn.setAndValidateAST(ast);
102
+ if (errors.length) {
103
+ var errorStr = errors.join('\n');
104
+ throw new Error(errorStr);
105
+ }
106
+ return reactFn;
107
+ }
108
+
109
+ module.exports = fstrDefine;
110
+ module.exports.selectFirst = selectFirst;
package/lib/id.js ADDED
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ var startingId = 0;
4
+
5
+ function createUniqueId() {
6
+ startingId += 1;
7
+ return startingId;
8
+ }
9
+
10
+ exports.createUniqueId = createUniqueId;
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ var defaultExecOptions = {
4
+ reactExecOptions: true,
5
+ outputStyle: 'callback',
6
+ };
7
+
8
+ var OUTPUT_STYLES = {
9
+ CALLBACK: 'callback',
10
+ NONE: 'none'
11
+ };
12
+
13
+ function isExecOptions(x) { return (x && x.reactExecOptions); }
14
+ function execOptionsFilter(x) { return isExecOptions(x); }
15
+ function nonExecOptionsFilter(x) { return !isExecOptions(x); }
16
+ function mergeExecOptions(accum, options) {
17
+ Object.keys(options).forEach(function (k) { accum[k] = options[k]; });
18
+ return accum;
19
+ }
20
+
21
+ function splitArgs(args, inParams, style) {
22
+ var result = { };
23
+ result.args = inParams.map(function (p) { return args.shift(); }); // take args for input params first
24
+ if (style === OUTPUT_STYLES.CALLBACK && args.length) result.cb = args.shift(); // next take the cb
25
+ result.extra = args; // these remaining were after the callback
26
+ return result;
27
+ }
28
+
29
+ function inputParser(inputArgs, ast) {
30
+ var parsedInput = { };
31
+ var execOptionsArr = inputArgs.filter(execOptionsFilter);
32
+ execOptionsArr.unshift(defaultExecOptions);
33
+ parsedInput.options = execOptionsArr.reduce(mergeExecOptions, {});
34
+
35
+ var args = inputArgs.filter(nonExecOptionsFilter);
36
+ var splitResult = splitArgs(args, ast.inParams, parsedInput.options.outputStyle);
37
+ parsedInput.args = splitResult.args;
38
+ parsedInput.cb = splitResult.cb;
39
+ if (splitResult.extra) parsedInput.extraArgs = splitResult.extra;
40
+ return parsedInput;
41
+ }
42
+
43
+ module.exports = inputParser;
44
+ module.exports.defaultExecOptions = defaultExecOptions;