@logicflow/engine 0.0.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/README.md +22 -0
- package/cjs/EventEmitter.js +70 -0
- package/cjs/FlowModel.js +252 -0
- package/cjs/Scheduler.js +278 -0
- package/cjs/constant/LogCode.js +31 -0
- package/cjs/constant/constant.js +23 -0
- package/cjs/expression/browserVm.js +69 -0
- package/cjs/expression/index.js +63 -0
- package/cjs/expression/nodeVm.js +53 -0
- package/cjs/index.js +161 -0
- package/cjs/nodes/BaseNode.js +243 -0
- package/cjs/nodes/StartNode.js +27 -0
- package/cjs/nodes/TaskNode.js +27 -0
- package/cjs/recorder/index.js +112 -0
- package/cjs/util/ID.js +12 -0
- package/cjs/util/global.js +32 -0
- package/cjs/util/storage.js +41 -0
- package/es/EventEmitter.d.ts +7 -0
- package/es/EventEmitter.js +68 -0
- package/es/FlowModel.d.ts +104 -0
- package/es/FlowModel.js +250 -0
- package/es/Scheduler.d.ts +51 -0
- package/es/Scheduler.js +276 -0
- package/es/constant/LogCode.d.ts +12 -0
- package/es/constant/LogCode.js +28 -0
- package/es/constant/constant.d.ts +14 -0
- package/es/constant/constant.js +20 -0
- package/es/expression/browserVm.d.ts +2 -0
- package/es/expression/browserVm.js +66 -0
- package/es/expression/index.d.ts +2 -0
- package/es/expression/index.js +60 -0
- package/es/expression/nodeVm.d.ts +2 -0
- package/es/expression/nodeVm.js +50 -0
- package/es/index.d.ts +47 -0
- package/es/index.js +156 -0
- package/es/nodes/BaseNode.d.ts +109 -0
- package/es/nodes/BaseNode.js +241 -0
- package/es/nodes/StartNode.d.ts +5 -0
- package/es/nodes/StartNode.js +25 -0
- package/es/nodes/TaskNode.d.ts +5 -0
- package/es/nodes/TaskNode.js +25 -0
- package/es/recorder/index.d.ts +9 -0
- package/es/recorder/index.js +110 -0
- package/es/util/ID.d.ts +2 -0
- package/es/util/ID.js +9 -0
- package/es/util/global.d.ts +5 -0
- package/es/util/global.js +26 -0
- package/es/util/storage.d.ts +6 -0
- package/es/util/storage.js +39 -0
- package/lib/main.js +1 -0
- package/package.json +87 -0
- package/types/EventEmitter.d.ts +7 -0
- package/types/FlowModel.d.ts +104 -0
- package/types/Scheduler.d.ts +51 -0
- package/types/constant/LogCode.d.ts +12 -0
- package/types/constant/constant.d.ts +14 -0
- package/types/expression/browserVm.d.ts +2 -0
- package/types/expression/index.d.ts +2 -0
- package/types/expression/nodeVm.d.ts +2 -0
- package/types/index.d.ts +47 -0
- package/types/nodes/BaseNode.d.ts +109 -0
- package/types/nodes/StartNode.d.ts +5 -0
- package/types/nodes/TaskNode.d.ts +5 -0
- package/types/recorder/index.d.ts +9 -0
- package/types/util/ID.d.ts +2 -0
- package/types/util/global.d.ts +5 -0
- package/types/util/storage.d.ts +6 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (_) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
var storage_1 = require("../util/storage");
|
|
40
|
+
var LOGICFLOW_ENGINE_INSTANCES = 'LOGICFLOW_ENGINE_INSTANCES';
|
|
41
|
+
var Recorder = /** @class */ (function () {
|
|
42
|
+
function Recorder() {
|
|
43
|
+
}
|
|
44
|
+
/*
|
|
45
|
+
* @param {Object} task
|
|
46
|
+
* {
|
|
47
|
+
* taskId: '',
|
|
48
|
+
* nodeId: '',
|
|
49
|
+
* executionId: '',
|
|
50
|
+
* nodeType: '',
|
|
51
|
+
* timestamp: '',
|
|
52
|
+
* properties: {},
|
|
53
|
+
* }
|
|
54
|
+
*/
|
|
55
|
+
Recorder.prototype.addTask = function (task) {
|
|
56
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
57
|
+
var executionId, taskId, instanceData;
|
|
58
|
+
return __generator(this, function (_a) {
|
|
59
|
+
switch (_a.label) {
|
|
60
|
+
case 0:
|
|
61
|
+
executionId = task.executionId, taskId = task.taskId;
|
|
62
|
+
return [4 /*yield*/, this.getExecutionTasks(executionId)];
|
|
63
|
+
case 1:
|
|
64
|
+
instanceData = _a.sent();
|
|
65
|
+
if (!instanceData) {
|
|
66
|
+
this.pushExecution(executionId);
|
|
67
|
+
}
|
|
68
|
+
this.pushTaskToExecution(executionId, taskId);
|
|
69
|
+
storage_1.default.setItem(taskId, task);
|
|
70
|
+
return [2 /*return*/];
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
Recorder.prototype.getTask = function (taskId) {
|
|
76
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
77
|
+
return __generator(this, function (_a) {
|
|
78
|
+
return [2 /*return*/, storage_1.default.getItem(taskId)];
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
Recorder.prototype.getExecutionTasks = function (executionId) {
|
|
83
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
84
|
+
return __generator(this, function (_a) {
|
|
85
|
+
return [2 /*return*/, storage_1.default.getItem(executionId)];
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
Recorder.prototype.clear = function () {
|
|
90
|
+
var instance = storage_1.default.getItem(LOGICFLOW_ENGINE_INSTANCES) || [];
|
|
91
|
+
instance.forEach(function (executionId) {
|
|
92
|
+
storage_1.default.removeItem(executionId);
|
|
93
|
+
var instanceData = storage_1.default.getItem(executionId) || [];
|
|
94
|
+
instanceData.forEach(function (taskId) {
|
|
95
|
+
storage_1.default.removeItem(taskId);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
storage_1.default.removeItem(LOGICFLOW_ENGINE_INSTANCES);
|
|
99
|
+
};
|
|
100
|
+
Recorder.prototype.pushExecution = function (executionId) {
|
|
101
|
+
var instance = storage_1.default.getItem(LOGICFLOW_ENGINE_INSTANCES) || [];
|
|
102
|
+
instance.push(executionId);
|
|
103
|
+
storage_1.default.setItem(LOGICFLOW_ENGINE_INSTANCES, instance);
|
|
104
|
+
};
|
|
105
|
+
Recorder.prototype.pushTaskToExecution = function (executionId, taskId) {
|
|
106
|
+
var tasks = storage_1.default.getItem(executionId) || [];
|
|
107
|
+
tasks.push(taskId);
|
|
108
|
+
storage_1.default.setItem(executionId, tasks);
|
|
109
|
+
};
|
|
110
|
+
return Recorder;
|
|
111
|
+
}());
|
|
112
|
+
exports.default = Recorder;
|
package/cjs/util/ID.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createTaskId = exports.createExecId = void 0;
|
|
4
|
+
var uuid_1 = require("uuid");
|
|
5
|
+
exports.createExecId = function () {
|
|
6
|
+
var uuid = uuid_1.v4();
|
|
7
|
+
return "exec-" + uuid;
|
|
8
|
+
};
|
|
9
|
+
exports.createTaskId = function () {
|
|
10
|
+
var uuid = uuid_1.v4();
|
|
11
|
+
return "task-" + uuid;
|
|
12
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// The one and only way of getting global scope in all environments
|
|
3
|
+
// https://stackoverflow.com/q/3277182/1008999
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.isInNodeJS = exports.isInWebWorker = exports.isInBrowser = exports.globalScope = void 0;
|
|
6
|
+
var isInBrowser = typeof window === 'object' && window.window === window;
|
|
7
|
+
exports.isInBrowser = isInBrowser;
|
|
8
|
+
var isInNodeJS = typeof global === 'object' && global.global === global;
|
|
9
|
+
exports.isInNodeJS = isInNodeJS;
|
|
10
|
+
// eslint-disable-next-line no-restricted-globals
|
|
11
|
+
var isInWebWorker = !isInBrowser && typeof self === 'object' && self.constructor;
|
|
12
|
+
exports.isInWebWorker = isInWebWorker;
|
|
13
|
+
var globalScope = (function () {
|
|
14
|
+
if (isInBrowser) {
|
|
15
|
+
return window;
|
|
16
|
+
}
|
|
17
|
+
// eslint-disable-next-line no-restricted-globals
|
|
18
|
+
if (typeof self === 'object' && self.self === self) { // web workers
|
|
19
|
+
// eslint-disable-next-line no-restricted-globals
|
|
20
|
+
return self;
|
|
21
|
+
}
|
|
22
|
+
if (isInNodeJS) {
|
|
23
|
+
return global;
|
|
24
|
+
}
|
|
25
|
+
if (typeof globalThis === 'object') {
|
|
26
|
+
return globalThis;
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
eval: function () { return undefined; },
|
|
30
|
+
};
|
|
31
|
+
})();
|
|
32
|
+
exports.globalScope = globalScope;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* 存储执行记录
|
|
5
|
+
*/
|
|
6
|
+
var global_1 = require("./global");
|
|
7
|
+
if (!global_1.globalScope.sessionStorage) {
|
|
8
|
+
var storage_1 = {
|
|
9
|
+
data: {},
|
|
10
|
+
setItem: function (key, value) {
|
|
11
|
+
storage_1.data[key] = value;
|
|
12
|
+
},
|
|
13
|
+
getItem: function (key) {
|
|
14
|
+
return storage_1.data[key];
|
|
15
|
+
},
|
|
16
|
+
removeItem: function (key) {
|
|
17
|
+
delete storage_1.data[key];
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
global_1.globalScope.sessionStorage = storage_1;
|
|
21
|
+
}
|
|
22
|
+
exports.default = {
|
|
23
|
+
setItem: function (key, value) {
|
|
24
|
+
if (typeof value === 'object') {
|
|
25
|
+
value = JSON.stringify(value);
|
|
26
|
+
}
|
|
27
|
+
global_1.globalScope.sessionStorage.setItem(key, value);
|
|
28
|
+
},
|
|
29
|
+
getItem: function (key) {
|
|
30
|
+
var value = global_1.globalScope.sessionStorage.getItem(key);
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(value);
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
removeItem: function (key) {
|
|
39
|
+
global_1.globalScope.sessionStorage.removeItem(key);
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
var EventEmitter = /** @class */ (function () {
|
|
2
|
+
function EventEmitter() {
|
|
3
|
+
this._events = {};
|
|
4
|
+
}
|
|
5
|
+
EventEmitter.prototype.on = function (evKey, callback, once) {
|
|
6
|
+
if (once === void 0) { once = false; }
|
|
7
|
+
evKey = evKey.trim();
|
|
8
|
+
if (!this._events[evKey]) {
|
|
9
|
+
this._events[evKey] = [];
|
|
10
|
+
}
|
|
11
|
+
this._events[evKey].push({
|
|
12
|
+
callback: callback,
|
|
13
|
+
once: !!once,
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
EventEmitter.prototype.emit = function (evt, eventArgs) {
|
|
17
|
+
var _this = this;
|
|
18
|
+
var events = this._events[evt] || [];
|
|
19
|
+
// 实际的处理 emit 方法
|
|
20
|
+
var doEmit = function (es) {
|
|
21
|
+
var length = es.length;
|
|
22
|
+
for (var i = 0; i < length; i++) {
|
|
23
|
+
if (!es[i]) {
|
|
24
|
+
// eslint-disable-next-line no-continue
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
var _a = es[i], callback = _a.callback, once = _a.once;
|
|
28
|
+
if (once) {
|
|
29
|
+
es.splice(i, 1);
|
|
30
|
+
if (es.length === 0) {
|
|
31
|
+
delete _this._events[evt];
|
|
32
|
+
}
|
|
33
|
+
length--;
|
|
34
|
+
i--;
|
|
35
|
+
}
|
|
36
|
+
callback.apply(_this, [eventArgs]);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
doEmit(events);
|
|
40
|
+
};
|
|
41
|
+
EventEmitter.prototype.off = function (evt, callback) {
|
|
42
|
+
if (!evt) {
|
|
43
|
+
// evt 为空全部清除
|
|
44
|
+
this._events = {};
|
|
45
|
+
}
|
|
46
|
+
if (!callback) {
|
|
47
|
+
// evt 存在,callback 为空,清除事件所有方法
|
|
48
|
+
delete this._events[evt];
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
// evt 存在,callback 存在,清除匹配的
|
|
52
|
+
var events = this._events[evt] || [];
|
|
53
|
+
var length_1 = events.length;
|
|
54
|
+
for (var i = 0; i < length_1; i++) {
|
|
55
|
+
if (events[i].callback === callback) {
|
|
56
|
+
events.splice(i, 1);
|
|
57
|
+
length_1--;
|
|
58
|
+
i--;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (events.length === 0) {
|
|
62
|
+
delete this._events[evt];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
return EventEmitter;
|
|
67
|
+
}());
|
|
68
|
+
export default EventEmitter;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { NodeConfig, NodeConstructor } from './nodes/BaseNode';
|
|
2
|
+
import type Recorder from './recorder';
|
|
3
|
+
import Scheduler from './Scheduler';
|
|
4
|
+
import type { TaskParam } from './types.d';
|
|
5
|
+
export declare type FlowResult = {
|
|
6
|
+
result?: Record<string, any>;
|
|
7
|
+
} & TaskParam;
|
|
8
|
+
export declare type TaskParams = {
|
|
9
|
+
executionId?: string;
|
|
10
|
+
taskId?: string;
|
|
11
|
+
nodeId?: string;
|
|
12
|
+
data?: Record<string, any>;
|
|
13
|
+
};
|
|
14
|
+
export declare type ExecParams = {
|
|
15
|
+
callback?: (result: FlowResult) => void;
|
|
16
|
+
onError?: (error: Error) => void;
|
|
17
|
+
} & TaskParams;
|
|
18
|
+
export default class FlowModel {
|
|
19
|
+
/**
|
|
20
|
+
* 流程支持的节点类型
|
|
21
|
+
*/
|
|
22
|
+
nodeModelMap: Map<string, NodeConstructor>;
|
|
23
|
+
/**
|
|
24
|
+
* 每一次执行流程都会生成一个唯一的executionId。
|
|
25
|
+
*/
|
|
26
|
+
executionId: string;
|
|
27
|
+
/**
|
|
28
|
+
* 调度器,用于调度节点的执行。
|
|
29
|
+
*/
|
|
30
|
+
scheduler: Scheduler;
|
|
31
|
+
/**
|
|
32
|
+
* 待执行的队列,当流程正在执行时,如果再次触发执行。那么会将执行参数放入到队列中,等待上一次执行完成后再执行。
|
|
33
|
+
*/
|
|
34
|
+
executeQueue: ExecParams[];
|
|
35
|
+
/**
|
|
36
|
+
* 当前正在执行。当监听到调度器执行完成时,出触发执行参数中的回调,告知外部执行完成。
|
|
37
|
+
*/
|
|
38
|
+
executingInstance: ExecParams;
|
|
39
|
+
/**
|
|
40
|
+
* 当前流程模型中的所有节点,边会被转换成节点的incoming和outgoing属性。
|
|
41
|
+
*/
|
|
42
|
+
nodeConfigMap: Map<string, NodeConfig>;
|
|
43
|
+
/**
|
|
44
|
+
* 当流程正在执行时,如果再次触发执行。那么会将执行参数放入到队列中,等待上一次执行完成后再执行。
|
|
45
|
+
*/
|
|
46
|
+
isRunning: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* 开始节点类型,在执行流程时,会从这些节点开始执行。
|
|
49
|
+
*/
|
|
50
|
+
startNodeType: string;
|
|
51
|
+
/**
|
|
52
|
+
* 当前流程中开始节点组成的数组。
|
|
53
|
+
*/
|
|
54
|
+
startNodes: NodeConfig[];
|
|
55
|
+
/**
|
|
56
|
+
* 用于存储全局数据,可以在流程中共享。
|
|
57
|
+
*/
|
|
58
|
+
globalData: Record<string, any>;
|
|
59
|
+
/**
|
|
60
|
+
* 外部传入的上下文,最终会传递给每个节点
|
|
61
|
+
* 例如:
|
|
62
|
+
* const context = {
|
|
63
|
+
* request: {
|
|
64
|
+
* get: (url) => {
|
|
65
|
+
* return fetch(url);
|
|
66
|
+
* }
|
|
67
|
+
* }
|
|
68
|
+
* 在节点内部可以通过 this.context.request.get(url) 来调用。
|
|
69
|
+
*/
|
|
70
|
+
context: Record<string, any>;
|
|
71
|
+
constructor({ nodeModelMap, recorder, context, globalData, startNodeType, }: {
|
|
72
|
+
nodeModelMap: Map<string, NodeConstructor>;
|
|
73
|
+
recorder: Recorder;
|
|
74
|
+
context?: Record<string, any>;
|
|
75
|
+
globalData?: Record<string, any>;
|
|
76
|
+
startNodeType?: string;
|
|
77
|
+
});
|
|
78
|
+
setStartNodeType(startNodeType: any): void;
|
|
79
|
+
load(graphData: any): void;
|
|
80
|
+
/**
|
|
81
|
+
* 执行流程
|
|
82
|
+
* 同一次执行,这次执行内部的节点执行顺序为并行。
|
|
83
|
+
* 多次执行,多次执行之间为串行。
|
|
84
|
+
* 允许一个流程多次执行,效率更高。
|
|
85
|
+
* 例如:
|
|
86
|
+
* 一个流程存在着两个开始节点,A和B,A和B的下一个节点都是C,C的下两个节点是D和E。
|
|
87
|
+
* 外部分别触发了A和B的执行,那么A和B的执行是串行的(也就是需要A执行完成后再执行B),但是D和E的执行是并行的。
|
|
88
|
+
* 如果希望A和B的执行是并行的,就不能使用同一个流程模型执行,应该初始化两个。
|
|
89
|
+
*/
|
|
90
|
+
execute(params: ExecParams): Promise<void>;
|
|
91
|
+
resume(params: ExecParams): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* 创建节点实例
|
|
94
|
+
* @param nodeId 节点Id
|
|
95
|
+
* @returns 节点示例
|
|
96
|
+
*/
|
|
97
|
+
createTask(nodeId: string): import("./nodes/BaseNode").default;
|
|
98
|
+
/**
|
|
99
|
+
* 更新流程全局数据
|
|
100
|
+
*/
|
|
101
|
+
updateGlobalData(data: any): void;
|
|
102
|
+
private onTaskFinished;
|
|
103
|
+
private createExecution;
|
|
104
|
+
}
|
package/es/FlowModel.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
13
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
14
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
15
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
16
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
17
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
18
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
22
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
23
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
24
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
25
|
+
function step(op) {
|
|
26
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
27
|
+
while (_) try {
|
|
28
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
29
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
30
|
+
switch (op[0]) {
|
|
31
|
+
case 0: case 1: t = op; break;
|
|
32
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
33
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
34
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
35
|
+
default:
|
|
36
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
37
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
38
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
39
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
40
|
+
if (t[2]) _.ops.pop();
|
|
41
|
+
_.trys.pop(); continue;
|
|
42
|
+
}
|
|
43
|
+
op = body.call(thisArg, _);
|
|
44
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
45
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
import { EVENT_INSTANCE_COMPLETE, EVENT_INSTANCE_INTERRUPTED, } from './constant/constant';
|
|
49
|
+
import { createExecId } from './util/ID';
|
|
50
|
+
import Scheduler from './Scheduler';
|
|
51
|
+
import { ErrorCode, getErrorMsg } from './constant/LogCode';
|
|
52
|
+
var FlowModel = /** @class */ (function () {
|
|
53
|
+
function FlowModel(_a) {
|
|
54
|
+
var _this = this;
|
|
55
|
+
var nodeModelMap = _a.nodeModelMap, recorder = _a.recorder, _b = _a.context, context = _b === void 0 ? {} : _b, _c = _a.globalData, globalData = _c === void 0 ? {} : _c, _d = _a.startNodeType, startNodeType = _d === void 0 ? 'StartNode' : _d;
|
|
56
|
+
/**
|
|
57
|
+
* 当前流程模型中的所有节点,边会被转换成节点的incoming和outgoing属性。
|
|
58
|
+
*/
|
|
59
|
+
this.nodeConfigMap = new Map();
|
|
60
|
+
/**
|
|
61
|
+
* 当前流程中开始节点组成的数组。
|
|
62
|
+
*/
|
|
63
|
+
this.startNodes = [];
|
|
64
|
+
/**
|
|
65
|
+
* 用于存储全局数据,可以在流程中共享。
|
|
66
|
+
*/
|
|
67
|
+
this.globalData = {};
|
|
68
|
+
// 流程包含的节点类型
|
|
69
|
+
this.nodeModelMap = nodeModelMap;
|
|
70
|
+
// 需要执行的队列
|
|
71
|
+
this.executeQueue = [];
|
|
72
|
+
// 执行中的任务
|
|
73
|
+
this.executingInstance = null;
|
|
74
|
+
// 外部传入的上下文,最终会传递给每个节点
|
|
75
|
+
this.context = context;
|
|
76
|
+
// 用于存储全局数据,可以在流程中共享。
|
|
77
|
+
this.globalData = globalData;
|
|
78
|
+
// 开始节点类型,在执行流程时,会从这些节点开始执行。
|
|
79
|
+
this.startNodeType = startNodeType;
|
|
80
|
+
this.isRunning = false;
|
|
81
|
+
this.scheduler = new Scheduler({
|
|
82
|
+
flowModel: this,
|
|
83
|
+
recorder: recorder,
|
|
84
|
+
});
|
|
85
|
+
this.scheduler.on(EVENT_INSTANCE_COMPLETE, function (result) {
|
|
86
|
+
_this.onTaskFinished(result);
|
|
87
|
+
});
|
|
88
|
+
this.scheduler.on(EVENT_INSTANCE_INTERRUPTED, function (result) {
|
|
89
|
+
_this.onTaskFinished(result);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
FlowModel.prototype.setStartNodeType = function (startNodeType) {
|
|
93
|
+
this.startNodeType = startNodeType;
|
|
94
|
+
};
|
|
95
|
+
FlowModel.prototype.load = function (graphData) {
|
|
96
|
+
var _this = this;
|
|
97
|
+
var _a = graphData.nodes, nodes = _a === void 0 ? [] : _a, _b = graphData.edges, edges = _b === void 0 ? [] : _b;
|
|
98
|
+
nodes.forEach(function (node) {
|
|
99
|
+
if (_this.nodeModelMap.has(node.type)) {
|
|
100
|
+
var nodeConfig = {
|
|
101
|
+
id: node.id,
|
|
102
|
+
type: node.type,
|
|
103
|
+
properties: node.properties,
|
|
104
|
+
incoming: [],
|
|
105
|
+
outgoing: [],
|
|
106
|
+
};
|
|
107
|
+
_this.nodeConfigMap.set(node.id, nodeConfig);
|
|
108
|
+
if (node.type === _this.startNodeType) {
|
|
109
|
+
_this.startNodes.push(nodeConfig);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
console.warn("\u672A\u8BC6\u522B\u7684\u8282\u70B9\u7C7B\u578B: " + node.type);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
edges.forEach(function (edge) {
|
|
117
|
+
var sourceNode = _this.nodeConfigMap.get(edge.sourceNodeId);
|
|
118
|
+
var targetNode = _this.nodeConfigMap.get(edge.targetNodeId);
|
|
119
|
+
if (sourceNode) {
|
|
120
|
+
sourceNode.outgoing.push({
|
|
121
|
+
id: edge.id,
|
|
122
|
+
properties: edge.properties,
|
|
123
|
+
target: edge.targetNodeId,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (targetNode && targetNode.type !== _this.startNodeType) {
|
|
127
|
+
targetNode.incoming.push({
|
|
128
|
+
id: edge.id,
|
|
129
|
+
properties: edge.properties,
|
|
130
|
+
source: edge.sourceNodeId,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* 执行流程
|
|
137
|
+
* 同一次执行,这次执行内部的节点执行顺序为并行。
|
|
138
|
+
* 多次执行,多次执行之间为串行。
|
|
139
|
+
* 允许一个流程多次执行,效率更高。
|
|
140
|
+
* 例如:
|
|
141
|
+
* 一个流程存在着两个开始节点,A和B,A和B的下一个节点都是C,C的下两个节点是D和E。
|
|
142
|
+
* 外部分别触发了A和B的执行,那么A和B的执行是串行的(也就是需要A执行完成后再执行B),但是D和E的执行是并行的。
|
|
143
|
+
* 如果希望A和B的执行是并行的,就不能使用同一个流程模型执行,应该初始化两个。
|
|
144
|
+
*/
|
|
145
|
+
FlowModel.prototype.execute = function (params) {
|
|
146
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
147
|
+
return __generator(this, function (_a) {
|
|
148
|
+
this.executeQueue.push(params);
|
|
149
|
+
if (this.isRunning) {
|
|
150
|
+
return [2 /*return*/];
|
|
151
|
+
}
|
|
152
|
+
this.isRunning = true;
|
|
153
|
+
this.createExecution();
|
|
154
|
+
return [2 /*return*/];
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
};
|
|
158
|
+
FlowModel.prototype.resume = function (params) {
|
|
159
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
160
|
+
return __generator(this, function (_a) {
|
|
161
|
+
this.executeQueue.push(params);
|
|
162
|
+
if (this.isRunning) {
|
|
163
|
+
return [2 /*return*/];
|
|
164
|
+
}
|
|
165
|
+
this.isRunning = true;
|
|
166
|
+
this.createExecution();
|
|
167
|
+
return [2 /*return*/];
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* 创建节点实例
|
|
173
|
+
* @param nodeId 节点Id
|
|
174
|
+
* @returns 节点示例
|
|
175
|
+
*/
|
|
176
|
+
FlowModel.prototype.createTask = function (nodeId) {
|
|
177
|
+
var nodeConfig = this.nodeConfigMap.get(nodeId);
|
|
178
|
+
var NodeModel = this.nodeModelMap.get(nodeConfig.type);
|
|
179
|
+
var task = new NodeModel({
|
|
180
|
+
nodeConfig: nodeConfig,
|
|
181
|
+
globalData: this.globalData,
|
|
182
|
+
context: this.context,
|
|
183
|
+
});
|
|
184
|
+
return task;
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* 更新流程全局数据
|
|
188
|
+
*/
|
|
189
|
+
FlowModel.prototype.updateGlobalData = function (data) {
|
|
190
|
+
this.globalData = __assign(__assign({}, this.globalData), data);
|
|
191
|
+
};
|
|
192
|
+
FlowModel.prototype.onTaskFinished = function (result) {
|
|
193
|
+
var executionId = result.executionId;
|
|
194
|
+
if (executionId !== this.executionId) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
var callback = this.executingInstance.callback;
|
|
198
|
+
if (callback) {
|
|
199
|
+
callback(result);
|
|
200
|
+
}
|
|
201
|
+
this.executingInstance = null;
|
|
202
|
+
if (this.executeQueue.length > 0) {
|
|
203
|
+
this.createExecution();
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
this.isRunning = false;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
FlowModel.prototype.createExecution = function () {
|
|
210
|
+
var _this = this;
|
|
211
|
+
var execParams = this.executeQueue.shift();
|
|
212
|
+
this.executingInstance = execParams;
|
|
213
|
+
if (execParams.executionId) {
|
|
214
|
+
this.executionId = execParams.executionId;
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
this.executionId = createExecId();
|
|
218
|
+
}
|
|
219
|
+
// 如果有taskId,那么表示恢复执行
|
|
220
|
+
if (execParams.taskId) {
|
|
221
|
+
this.scheduler.resume({
|
|
222
|
+
executionId: this.executionId,
|
|
223
|
+
taskId: execParams.taskId,
|
|
224
|
+
nodeId: execParams.nodeId,
|
|
225
|
+
data: execParams.data,
|
|
226
|
+
});
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (execParams.nodeId) {
|
|
230
|
+
var nodeConfig = this.nodeConfigMap.get(execParams.nodeId);
|
|
231
|
+
if (!nodeConfig) {
|
|
232
|
+
execParams.onError(new Error(getErrorMsg(ErrorCode.NONE_NODE_ID) + "(" + execParams.nodeId + ")"));
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
this.startNodes = [nodeConfig];
|
|
236
|
+
}
|
|
237
|
+
this.startNodes.forEach(function (startNode) {
|
|
238
|
+
_this.scheduler.addTask({
|
|
239
|
+
executionId: _this.executionId,
|
|
240
|
+
nodeId: startNode.id,
|
|
241
|
+
});
|
|
242
|
+
// 所有的开始节点都执行
|
|
243
|
+
});
|
|
244
|
+
this.scheduler.run({
|
|
245
|
+
executionId: this.executionId,
|
|
246
|
+
});
|
|
247
|
+
};
|
|
248
|
+
return FlowModel;
|
|
249
|
+
}());
|
|
250
|
+
export default FlowModel;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import EventEmitter from './EventEmitter';
|
|
2
|
+
import type { TaskParam, NodeParam, ResumeParam } from './types.d';
|
|
3
|
+
import type FlowModel from './FlowModel';
|
|
4
|
+
import type Recorder from './recorder';
|
|
5
|
+
declare type TaskParamMap = Map<string, TaskParam>;
|
|
6
|
+
/**
|
|
7
|
+
* 调度器
|
|
8
|
+
* 通过一个队列维护需要执行的节点,一个集合维护正在执行的节点
|
|
9
|
+
*/
|
|
10
|
+
export default class Scheduler extends EventEmitter {
|
|
11
|
+
nodeQueueMap: Map<string, NodeParam[]>;
|
|
12
|
+
taskRunningMap: Map<string, TaskParamMap>;
|
|
13
|
+
flowModel: FlowModel;
|
|
14
|
+
recorder: Recorder;
|
|
15
|
+
currentTask: TaskParam | null;
|
|
16
|
+
constructor(config: any);
|
|
17
|
+
/**
|
|
18
|
+
* 添加一个任务到队列中。
|
|
19
|
+
* 1. 由流程模型将所有的开始节点添加到队列中。
|
|
20
|
+
* 2. 当一个节点执行完成后,将后续的节点添加到队列中。
|
|
21
|
+
*/
|
|
22
|
+
addTask(nodeParam: NodeParam): void;
|
|
23
|
+
/**
|
|
24
|
+
* 调度器执行下一个任务
|
|
25
|
+
* 1. 提供给流程模型,用户开始执行第一个任务。
|
|
26
|
+
* 2. 内部任务执行完成后,调用此方法继续执行下一个任务。
|
|
27
|
+
* 3. 当判断没有可以继续执行的任务后,触发流程结束事件。
|
|
28
|
+
*/
|
|
29
|
+
run(runParams: {
|
|
30
|
+
executionId: string;
|
|
31
|
+
nodeId?: string;
|
|
32
|
+
taskId?: string;
|
|
33
|
+
}): void;
|
|
34
|
+
/**
|
|
35
|
+
* 恢复某个任务的执行。
|
|
36
|
+
* 可以自定义节点手动实现流程中断,然后通过此方法恢复流程的执行。
|
|
37
|
+
*/
|
|
38
|
+
resume(resumeParam: ResumeParam): Promise<void>;
|
|
39
|
+
stop(data: any): void;
|
|
40
|
+
private pushTaskToRunningMap;
|
|
41
|
+
private removeTaskFromRunningMap;
|
|
42
|
+
private hasRunningTask;
|
|
43
|
+
private exec;
|
|
44
|
+
private interrupted;
|
|
45
|
+
private next;
|
|
46
|
+
/**
|
|
47
|
+
* 为了防止多次添加导致
|
|
48
|
+
*/
|
|
49
|
+
private saveTaskResult;
|
|
50
|
+
}
|
|
51
|
+
export {};
|