ultravisor 1.0.2 → 1.0.4
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/.claude/launch.json +11 -0
- package/.claude/ultravisor-dev-config.json +3 -0
- package/.ultravisor.json +426 -0
- package/docs/README.md +63 -0
- package/package.json +12 -8
- package/source/Ultravisor.cjs +22 -3
- package/source/cli/Ultravisor-CLIProgram.cjs +35 -23
- package/source/cli/commands/Ultravisor-Command-SingleOperation.cjs +29 -18
- package/source/cli/commands/Ultravisor-Command-SingleTask.cjs +62 -19
- package/source/cli/commands/Ultravisor-Command-UpdateTask.cjs +27 -15
- package/source/config/Ultravisor-Default-Command-Configuration.cjs +5 -3
- package/source/services/Ultravisor-ExecutionEngine.cjs +1039 -0
- package/source/services/Ultravisor-ExecutionManifest.cjs +399 -0
- package/source/services/Ultravisor-Hypervisor-State.cjs +270 -97
- package/source/services/Ultravisor-Hypervisor.cjs +38 -83
- package/source/services/Ultravisor-StateManager.cjs +241 -0
- package/source/services/Ultravisor-TaskTypeRegistry.cjs +143 -0
- package/source/services/tasks/Ultravisor-TaskType-Base.cjs +105 -0
- package/source/services/tasks/control/Ultravisor-TaskType-IfConditional.cjs +148 -0
- package/source/services/tasks/control/Ultravisor-TaskType-LaunchOperation.cjs +187 -0
- package/source/services/tasks/control/Ultravisor-TaskType-SplitExecute.cjs +184 -0
- package/source/services/tasks/data/Ultravisor-TaskType-ReplaceString.cjs +82 -0
- package/source/services/tasks/data/Ultravisor-TaskType-SetValues.cjs +81 -0
- package/source/services/tasks/data/Ultravisor-TaskType-StringAppender.cjs +101 -0
- package/source/services/tasks/file-io/Ultravisor-TaskType-ReadFile.cjs +103 -0
- package/source/services/tasks/file-io/Ultravisor-TaskType-WriteFile.cjs +117 -0
- package/source/services/tasks/interaction/Ultravisor-TaskType-ErrorMessage.cjs +54 -0
- package/source/services/tasks/interaction/Ultravisor-TaskType-ValueInput.cjs +62 -0
- package/source/web_server/Ultravisor-API-Server.cjs +237 -124
- package/test/Ultravisor_browser_tests.js +2226 -0
- package/test/Ultravisor_tests.js +1143 -5830
- package/webinterface/css/ultravisor.css +23 -0
- package/webinterface/package.json +6 -3
- package/webinterface/source/Pict-Application-Ultravisor.js +93 -73
- package/webinterface/source/cards/FlowCard-CSVTransform.js +43 -0
- package/webinterface/source/cards/FlowCard-Command.js +86 -0
- package/webinterface/source/cards/FlowCard-ComprehensionIntersect.js +40 -0
- package/webinterface/source/cards/FlowCard-Conditional.js +87 -0
- package/webinterface/source/cards/FlowCard-CopyFile.js +55 -0
- package/webinterface/source/cards/FlowCard-End.js +29 -0
- package/webinterface/source/cards/FlowCard-GetJSON.js +55 -0
- package/webinterface/source/cards/FlowCard-GetText.js +54 -0
- package/webinterface/source/cards/FlowCard-Histogram.js +176 -0
- package/webinterface/source/cards/FlowCard-LaunchOperation.js +82 -0
- package/webinterface/source/cards/FlowCard-ListFiles.js +55 -0
- package/webinterface/source/cards/FlowCard-MeadowCount.js +44 -0
- package/webinterface/source/cards/FlowCard-MeadowCreate.js +44 -0
- package/webinterface/source/cards/FlowCard-MeadowDelete.js +45 -0
- package/webinterface/source/cards/FlowCard-MeadowRead.js +46 -0
- package/webinterface/source/cards/FlowCard-MeadowReads.js +46 -0
- package/webinterface/source/cards/FlowCard-MeadowUpdate.js +44 -0
- package/webinterface/source/cards/FlowCard-ParseCSV.js +85 -0
- package/webinterface/source/cards/FlowCard-ReadJSON.js +54 -0
- package/webinterface/source/cards/FlowCard-ReadText.js +54 -0
- package/webinterface/source/cards/FlowCard-RestRequest.js +59 -0
- package/webinterface/source/cards/FlowCard-SendJSON.js +57 -0
- package/webinterface/source/cards/FlowCard-Solver.js +77 -0
- package/webinterface/source/cards/FlowCard-Start.js +29 -0
- package/webinterface/source/cards/FlowCard-TemplateString.js +77 -0
- package/webinterface/source/cards/FlowCard-WriteJSON.js +54 -0
- package/webinterface/source/cards/FlowCard-WriteText.js +54 -0
- package/webinterface/source/data/ExampleFlow-CSVPipeline.js +231 -0
- package/webinterface/source/data/ExampleFlow-FileProcessor.js +315 -0
- package/webinterface/source/data/ExampleFlow-MeadowPipeline.js +328 -0
- package/webinterface/source/providers/PictRouter-Ultravisor-Configuration.json +8 -8
- package/webinterface/source/views/PictView-Ultravisor-Dashboard.js +6 -6
- package/webinterface/source/views/PictView-Ultravisor-FlowEditor.js +436 -0
- package/webinterface/source/views/PictView-Ultravisor-ManifestList.js +45 -43
- package/webinterface/source/views/PictView-Ultravisor-OperationEdit.js +34 -89
- package/webinterface/source/views/PictView-Ultravisor-OperationList.js +128 -13
- package/webinterface/source/views/PictView-Ultravisor-PendingInput.js +314 -0
- package/webinterface/source/views/PictView-Ultravisor-Schedule.js +18 -53
- package/webinterface/source/views/PictView-Ultravisor-TimingView.js +27 -14
- package/webinterface/source/views/PictView-Ultravisor-TopBar.js +2 -1
- package/.babelrc +0 -6
- package/.browserslistrc +0 -1
- package/.browserslistrc-BACKUP +0 -1
- package/.gulpfile-quackage-config.json +0 -7
- package/.gulpfile-quackage.js +0 -2
- package/debug/Harness.js +0 -5
- package/source/services/Ultravisor-Operation-Manifest.cjs +0 -160
- package/source/services/Ultravisor-Operation.cjs +0 -200
- package/source/services/Ultravisor-Task.cjs +0 -349
- package/source/services/events/Ultravisor-Hypervisor-Event-Solver.cjs +0 -11
- package/source/services/tasks/Ultravisor-Task-Base.cjs +0 -264
- package/source/services/tasks/Ultravisor-Task-CollectValues.cjs +0 -188
- package/source/services/tasks/Ultravisor-Task-Command.cjs +0 -65
- package/source/services/tasks/Ultravisor-Task-CommandEach.cjs +0 -190
- package/source/services/tasks/Ultravisor-Task-Conditional.cjs +0 -104
- package/source/services/tasks/Ultravisor-Task-DateWindow.cjs +0 -72
- package/source/services/tasks/Ultravisor-Task-GeneratePagedOperation.cjs +0 -336
- package/source/services/tasks/Ultravisor-Task-LaunchOperation.cjs +0 -143
- package/source/services/tasks/Ultravisor-Task-LaunchTask.cjs +0 -146
- package/source/services/tasks/Ultravisor-Task-LineMatch.cjs +0 -158
- package/source/services/tasks/Ultravisor-Task-Request.cjs +0 -56
- package/source/services/tasks/Ultravisor-Task-Solver.cjs +0 -89
- package/source/services/tasks/Ultravisor-Task-TemplateString.cjs +0 -93
- package/source/services/tasks/rest/Ultravisor-Task-GetBinary.cjs +0 -127
- package/source/services/tasks/rest/Ultravisor-Task-GetJSON.cjs +0 -119
- package/source/services/tasks/rest/Ultravisor-Task-GetText.cjs +0 -109
- package/source/services/tasks/rest/Ultravisor-Task-GetXML.cjs +0 -112
- package/source/services/tasks/rest/Ultravisor-Task-RestRequest.cjs +0 -499
- package/source/services/tasks/rest/Ultravisor-Task-SendJSON.cjs +0 -150
- package/source/services/tasks/stagingfiles/Ultravisor-Task-CopyFile.cjs +0 -110
- package/source/services/tasks/stagingfiles/Ultravisor-Task-ListFiles.cjs +0 -89
- package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadBinary.cjs +0 -87
- package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadJSON.cjs +0 -67
- package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadText.cjs +0 -66
- package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadXML.cjs +0 -69
- package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteBinary.cjs +0 -95
- package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteJSON.cjs +0 -96
- package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteText.cjs +0 -99
- package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteXML.cjs +0 -102
- package/webinterface/.babelrc +0 -6
- package/webinterface/.browserslistrc +0 -1
- package/webinterface/.browserslistrc-BACKUP +0 -1
- package/webinterface/.gulpfile-quackage-config.json +0 -7
- package/webinterface/.gulpfile-quackage.js +0 -2
- package/webinterface/source/views/PictView-Ultravisor-TaskEdit.js +0 -220
- package/webinterface/source/views/PictView-Ultravisor-TaskList.js +0 -248
- /package/docs/{cover.md → _cover.md} +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
const libPictService = require('pict-serviceproviderbase');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Manages three-level state (Global, Operation, Task) and provides
|
|
5
|
+
* address resolution through Manyfest.
|
|
6
|
+
*
|
|
7
|
+
* Address prefixes:
|
|
8
|
+
* Global.X -> GlobalState.X
|
|
9
|
+
* Operation.X -> OperationState.X
|
|
10
|
+
* Task.X -> TaskOutputs[currentNodeHash].X
|
|
11
|
+
* TaskOutput.{Hash}.X -> TaskOutputs[Hash].X
|
|
12
|
+
* Staging.Path -> StagingPath string
|
|
13
|
+
*/
|
|
14
|
+
class UltravisorStateManager extends libPictService
|
|
15
|
+
{
|
|
16
|
+
constructor(pPict, pOptions, pServiceHash)
|
|
17
|
+
{
|
|
18
|
+
super(pPict, pOptions, pServiceHash);
|
|
19
|
+
|
|
20
|
+
this.serviceType = 'UltravisorStateManager';
|
|
21
|
+
|
|
22
|
+
this._Manyfest = this.fable.newManyfest();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Resolve an address against the execution context.
|
|
27
|
+
*
|
|
28
|
+
* @param {string} pAddress - The address to resolve (e.g. 'Operation.InputFilePath').
|
|
29
|
+
* @param {object} pExecutionContext - The runtime execution context.
|
|
30
|
+
* @param {string} [pCurrentNodeHash] - The current task node's hash (for Task.X resolution).
|
|
31
|
+
* @returns {*} The resolved value, or undefined if not found.
|
|
32
|
+
*/
|
|
33
|
+
resolveAddress(pAddress, pExecutionContext, pCurrentNodeHash)
|
|
34
|
+
{
|
|
35
|
+
if (!pAddress || typeof(pAddress) !== 'string')
|
|
36
|
+
{
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let tmpDotIndex = pAddress.indexOf('.');
|
|
41
|
+
let tmpPrefix;
|
|
42
|
+
let tmpRemainder;
|
|
43
|
+
|
|
44
|
+
if (tmpDotIndex > -1)
|
|
45
|
+
{
|
|
46
|
+
tmpPrefix = pAddress.substring(0, tmpDotIndex);
|
|
47
|
+
tmpRemainder = pAddress.substring(tmpDotIndex + 1);
|
|
48
|
+
}
|
|
49
|
+
else
|
|
50
|
+
{
|
|
51
|
+
tmpPrefix = pAddress;
|
|
52
|
+
tmpRemainder = '';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
switch (tmpPrefix)
|
|
56
|
+
{
|
|
57
|
+
case 'Global':
|
|
58
|
+
return this._resolveFromObject(pExecutionContext.GlobalState, tmpRemainder);
|
|
59
|
+
|
|
60
|
+
case 'Operation':
|
|
61
|
+
return this._resolveFromObject(pExecutionContext.OperationState, tmpRemainder);
|
|
62
|
+
|
|
63
|
+
case 'Task':
|
|
64
|
+
if (!pCurrentNodeHash)
|
|
65
|
+
{
|
|
66
|
+
this.log.warn(`UltravisorStateManager: resolveAddress for Task.X requires a current node hash.`);
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
return this._resolveFromObject(
|
|
70
|
+
pExecutionContext.TaskOutputs[pCurrentNodeHash] || {},
|
|
71
|
+
tmpRemainder);
|
|
72
|
+
|
|
73
|
+
case 'TaskOutput':
|
|
74
|
+
{
|
|
75
|
+
// TaskOutput.{NodeHash}.{Path}
|
|
76
|
+
let tmpSecondDot = tmpRemainder.indexOf('.');
|
|
77
|
+
if (tmpSecondDot < 0)
|
|
78
|
+
{
|
|
79
|
+
// Just TaskOutput.{NodeHash} -- return the whole output object
|
|
80
|
+
return pExecutionContext.TaskOutputs[tmpRemainder] || {};
|
|
81
|
+
}
|
|
82
|
+
let tmpNodeHash = tmpRemainder.substring(0, tmpSecondDot);
|
|
83
|
+
let tmpPath = tmpRemainder.substring(tmpSecondDot + 1);
|
|
84
|
+
return this._resolveFromObject(
|
|
85
|
+
pExecutionContext.TaskOutputs[tmpNodeHash] || {},
|
|
86
|
+
tmpPath);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
case 'Staging':
|
|
90
|
+
if (tmpRemainder === 'Path' || tmpRemainder === '')
|
|
91
|
+
{
|
|
92
|
+
return pExecutionContext.StagingPath || '';
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
|
|
96
|
+
default:
|
|
97
|
+
// Fall through: try the full address against OperationState first,
|
|
98
|
+
// then GlobalState. This allows shorthand like 'InputFilePath'
|
|
99
|
+
// to resolve from Operation state.
|
|
100
|
+
let tmpValue = this._resolveFromObject(pExecutionContext.OperationState, pAddress);
|
|
101
|
+
if (tmpValue !== undefined)
|
|
102
|
+
{
|
|
103
|
+
return tmpValue;
|
|
104
|
+
}
|
|
105
|
+
return this._resolveFromObject(pExecutionContext.GlobalState, pAddress);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Set a value at an address in the execution context.
|
|
111
|
+
*
|
|
112
|
+
* @param {string} pAddress - The address to set (e.g. 'Operation.InputFilePath').
|
|
113
|
+
* @param {*} pValue - The value to set.
|
|
114
|
+
* @param {object} pExecutionContext - The runtime execution context.
|
|
115
|
+
* @param {string} [pCurrentNodeHash] - The current task node's hash (for Task.X).
|
|
116
|
+
* @returns {boolean} True if the value was set successfully.
|
|
117
|
+
*/
|
|
118
|
+
setAddress(pAddress, pValue, pExecutionContext, pCurrentNodeHash)
|
|
119
|
+
{
|
|
120
|
+
if (!pAddress || typeof(pAddress) !== 'string')
|
|
121
|
+
{
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let tmpDotIndex = pAddress.indexOf('.');
|
|
126
|
+
let tmpPrefix;
|
|
127
|
+
let tmpRemainder;
|
|
128
|
+
|
|
129
|
+
if (tmpDotIndex > -1)
|
|
130
|
+
{
|
|
131
|
+
tmpPrefix = pAddress.substring(0, tmpDotIndex);
|
|
132
|
+
tmpRemainder = pAddress.substring(tmpDotIndex + 1);
|
|
133
|
+
}
|
|
134
|
+
else
|
|
135
|
+
{
|
|
136
|
+
tmpPrefix = pAddress;
|
|
137
|
+
tmpRemainder = '';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
switch (tmpPrefix)
|
|
141
|
+
{
|
|
142
|
+
case 'Global':
|
|
143
|
+
return this._setOnObject(pExecutionContext.GlobalState, tmpRemainder, pValue);
|
|
144
|
+
|
|
145
|
+
case 'Operation':
|
|
146
|
+
return this._setOnObject(pExecutionContext.OperationState, tmpRemainder, pValue);
|
|
147
|
+
|
|
148
|
+
case 'Task':
|
|
149
|
+
if (!pCurrentNodeHash)
|
|
150
|
+
{
|
|
151
|
+
this.log.warn(`UltravisorStateManager: setAddress for Task.X requires a current node hash.`);
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
if (!pExecutionContext.TaskOutputs[pCurrentNodeHash])
|
|
155
|
+
{
|
|
156
|
+
pExecutionContext.TaskOutputs[pCurrentNodeHash] = {};
|
|
157
|
+
}
|
|
158
|
+
return this._setOnObject(
|
|
159
|
+
pExecutionContext.TaskOutputs[pCurrentNodeHash],
|
|
160
|
+
tmpRemainder, pValue);
|
|
161
|
+
|
|
162
|
+
case 'TaskOutput':
|
|
163
|
+
{
|
|
164
|
+
let tmpSecondDot = tmpRemainder.indexOf('.');
|
|
165
|
+
if (tmpSecondDot < 0)
|
|
166
|
+
{
|
|
167
|
+
// Setting the entire TaskOutput for a node
|
|
168
|
+
pExecutionContext.TaskOutputs[tmpRemainder] = pValue;
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
let tmpNodeHash = tmpRemainder.substring(0, tmpSecondDot);
|
|
172
|
+
let tmpPath = tmpRemainder.substring(tmpSecondDot + 1);
|
|
173
|
+
if (!pExecutionContext.TaskOutputs[tmpNodeHash])
|
|
174
|
+
{
|
|
175
|
+
pExecutionContext.TaskOutputs[tmpNodeHash] = {};
|
|
176
|
+
}
|
|
177
|
+
return this._setOnObject(
|
|
178
|
+
pExecutionContext.TaskOutputs[tmpNodeHash],
|
|
179
|
+
tmpPath, pValue);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
default:
|
|
183
|
+
// Default: write to OperationState
|
|
184
|
+
return this._setOnObject(pExecutionContext.OperationState, pAddress, pValue);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Build a root data object for Pict template resolution.
|
|
190
|
+
* This object can be passed to pict.parseTemplate as the record.
|
|
191
|
+
*
|
|
192
|
+
* @param {object} pExecutionContext - The runtime execution context.
|
|
193
|
+
* @param {*} [pSourceValue] - An optional source value (e.g. for state connection resolution).
|
|
194
|
+
* @returns {object} Root data object with all state levels.
|
|
195
|
+
*/
|
|
196
|
+
buildTemplateContext(pExecutionContext, pSourceValue)
|
|
197
|
+
{
|
|
198
|
+
return {
|
|
199
|
+
Value: pSourceValue,
|
|
200
|
+
Global: pExecutionContext.GlobalState || {},
|
|
201
|
+
Operation: pExecutionContext.OperationState || {},
|
|
202
|
+
TaskOutput: pExecutionContext.TaskOutputs || {},
|
|
203
|
+
Staging: { Path: pExecutionContext.StagingPath || '' }
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// --- Internal helpers ---
|
|
208
|
+
|
|
209
|
+
_resolveFromObject(pObject, pPath)
|
|
210
|
+
{
|
|
211
|
+
if (!pObject || typeof(pObject) !== 'object')
|
|
212
|
+
{
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (!pPath || pPath === '')
|
|
217
|
+
{
|
|
218
|
+
return pObject;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return this._Manyfest.getValueAtAddress(pObject, pPath);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
_setOnObject(pObject, pPath, pValue)
|
|
225
|
+
{
|
|
226
|
+
if (!pObject || typeof(pObject) !== 'object')
|
|
227
|
+
{
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (!pPath || pPath === '')
|
|
232
|
+
{
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
this._Manyfest.setValueAtAddress(pObject, pPath, pValue);
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
module.exports = UltravisorStateManager;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
const libPictService = require('pict-serviceproviderbase');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Registry of all available task type classes.
|
|
5
|
+
*
|
|
6
|
+
* Task types are registered by hash and can be instantiated on demand
|
|
7
|
+
* by the ExecutionEngine when a task node needs to execute.
|
|
8
|
+
*/
|
|
9
|
+
class UltravisorTaskTypeRegistry extends libPictService
|
|
10
|
+
{
|
|
11
|
+
constructor(pPict, pOptions, pServiceHash)
|
|
12
|
+
{
|
|
13
|
+
super(pPict, pOptions, pServiceHash);
|
|
14
|
+
|
|
15
|
+
this.serviceType = 'UltravisorTaskTypeRegistry';
|
|
16
|
+
|
|
17
|
+
// Map of task type hash -> task type class
|
|
18
|
+
this._TaskTypes = {};
|
|
19
|
+
|
|
20
|
+
// Map of task type hash -> definition (cached from class instances)
|
|
21
|
+
this._Definitions = {};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Register a task type class.
|
|
26
|
+
*
|
|
27
|
+
* @param {Function} pTaskTypeClass - A class extending UltravisorTaskType.
|
|
28
|
+
* @returns {object} The task type definition.
|
|
29
|
+
*/
|
|
30
|
+
registerTaskType(pTaskTypeClass)
|
|
31
|
+
{
|
|
32
|
+
if (typeof(pTaskTypeClass) !== 'function')
|
|
33
|
+
{
|
|
34
|
+
this.log.error('UltravisorTaskTypeRegistry: registerTaskType requires a constructor function.');
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Instantiate temporarily to read the definition
|
|
39
|
+
let tmpInstance = new pTaskTypeClass(this.fable, {}, `TaskType-Probe-${Date.now()}`);
|
|
40
|
+
let tmpDefinition = tmpInstance.definition;
|
|
41
|
+
|
|
42
|
+
if (!tmpDefinition || !tmpDefinition.Hash)
|
|
43
|
+
{
|
|
44
|
+
this.log.error('UltravisorTaskTypeRegistry: task type class must have a definition with a Hash.');
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this._TaskTypes[tmpDefinition.Hash] = pTaskTypeClass;
|
|
49
|
+
this._Definitions[tmpDefinition.Hash] = tmpDefinition;
|
|
50
|
+
|
|
51
|
+
this.log.info(`UltravisorTaskTypeRegistry: registered task type [${tmpDefinition.Hash}] "${tmpDefinition.Name}"`);
|
|
52
|
+
|
|
53
|
+
return tmpDefinition;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Create a new instance of a task type by hash.
|
|
58
|
+
*
|
|
59
|
+
* @param {string} pHash - The task type hash (e.g. 'read-file').
|
|
60
|
+
* @returns {object|null} A new instance of the task type, or null if not found.
|
|
61
|
+
*/
|
|
62
|
+
instantiateTaskType(pHash)
|
|
63
|
+
{
|
|
64
|
+
let tmpTaskTypeClass = this._TaskTypes[pHash];
|
|
65
|
+
|
|
66
|
+
if (!tmpTaskTypeClass)
|
|
67
|
+
{
|
|
68
|
+
this.log.error(`UltravisorTaskTypeRegistry: unknown task type hash [${pHash}]`);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return new tmpTaskTypeClass(this.fable, {}, `TaskType-${pHash}-${Date.now()}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get the definition for a task type by hash.
|
|
77
|
+
*
|
|
78
|
+
* @param {string} pHash - The task type hash.
|
|
79
|
+
* @returns {object|null} The definition, or null if not found.
|
|
80
|
+
*/
|
|
81
|
+
getDefinition(pHash)
|
|
82
|
+
{
|
|
83
|
+
return this._Definitions[pHash] || null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get all registered task type definitions.
|
|
88
|
+
*
|
|
89
|
+
* @returns {Array} Array of definition objects.
|
|
90
|
+
*/
|
|
91
|
+
listDefinitions()
|
|
92
|
+
{
|
|
93
|
+
let tmpDefinitions = [];
|
|
94
|
+
let tmpKeys = Object.keys(this._Definitions);
|
|
95
|
+
|
|
96
|
+
for (let i = 0; i < tmpKeys.length; i++)
|
|
97
|
+
{
|
|
98
|
+
tmpDefinitions.push(this._Definitions[tmpKeys[i]]);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return tmpDefinitions;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Check if a task type hash is registered.
|
|
106
|
+
*
|
|
107
|
+
* @param {string} pHash - The task type hash.
|
|
108
|
+
* @returns {boolean}
|
|
109
|
+
*/
|
|
110
|
+
hasTaskType(pHash)
|
|
111
|
+
{
|
|
112
|
+
return this._TaskTypes.hasOwnProperty(pHash);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Register all built-in task types.
|
|
117
|
+
* Called during application initialization.
|
|
118
|
+
*/
|
|
119
|
+
registerBuiltInTaskTypes()
|
|
120
|
+
{
|
|
121
|
+
// File I/O
|
|
122
|
+
this.registerTaskType(require('./tasks/file-io/Ultravisor-TaskType-ReadFile.cjs'));
|
|
123
|
+
this.registerTaskType(require('./tasks/file-io/Ultravisor-TaskType-WriteFile.cjs'));
|
|
124
|
+
|
|
125
|
+
// Data
|
|
126
|
+
this.registerTaskType(require('./tasks/data/Ultravisor-TaskType-SetValues.cjs'));
|
|
127
|
+
this.registerTaskType(require('./tasks/data/Ultravisor-TaskType-ReplaceString.cjs'));
|
|
128
|
+
this.registerTaskType(require('./tasks/data/Ultravisor-TaskType-StringAppender.cjs'));
|
|
129
|
+
|
|
130
|
+
// Control
|
|
131
|
+
this.registerTaskType(require('./tasks/control/Ultravisor-TaskType-IfConditional.cjs'));
|
|
132
|
+
this.registerTaskType(require('./tasks/control/Ultravisor-TaskType-SplitExecute.cjs'));
|
|
133
|
+
this.registerTaskType(require('./tasks/control/Ultravisor-TaskType-LaunchOperation.cjs'));
|
|
134
|
+
|
|
135
|
+
// Interaction
|
|
136
|
+
this.registerTaskType(require('./tasks/interaction/Ultravisor-TaskType-ValueInput.cjs'));
|
|
137
|
+
this.registerTaskType(require('./tasks/interaction/Ultravisor-TaskType-ErrorMessage.cjs'));
|
|
138
|
+
|
|
139
|
+
this.log.info(`UltravisorTaskTypeRegistry: ${Object.keys(this._TaskTypes).length} built-in task types registered.`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = UltravisorTaskTypeRegistry;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base class for all Ultravisor task types.
|
|
5
|
+
*
|
|
6
|
+
* Subclasses override `definition` to declare their port schema
|
|
7
|
+
* (EventInputs, EventOutputs, SettingsInputs, StateOutputs)
|
|
8
|
+
* and `execute` to implement the task logic.
|
|
9
|
+
*/
|
|
10
|
+
class UltravisorTaskType extends libFableServiceProviderBase
|
|
11
|
+
{
|
|
12
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
13
|
+
{
|
|
14
|
+
super(pFable, pOptions, pServiceHash);
|
|
15
|
+
|
|
16
|
+
this.serviceType = 'UltravisorTaskType';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Port schema and metadata for this task type.
|
|
21
|
+
* Subclasses MUST override this getter.
|
|
22
|
+
*
|
|
23
|
+
* @returns {object} Task type definition with:
|
|
24
|
+
* Hash {string} - unique type identifier (e.g. 'read-file')
|
|
25
|
+
* Name {string} - display name
|
|
26
|
+
* Description {string} - what the task does
|
|
27
|
+
* Category {string} - grouping key (file-io, data, control, interaction)
|
|
28
|
+
* EventInputs {Array} - [{ Name, Description? }]
|
|
29
|
+
* EventOutputs {Array} - [{ Name, Description?, IsError? }]
|
|
30
|
+
* SettingsInputs {Array} - [{ Name, DataType, Required?, Default?, Description? }]
|
|
31
|
+
* StateOutputs {Array} - [{ Name, DataType?, Description? }]
|
|
32
|
+
* DefaultSettings {object} - default values for SettingsInputs
|
|
33
|
+
*/
|
|
34
|
+
get definition()
|
|
35
|
+
{
|
|
36
|
+
return {
|
|
37
|
+
Hash: 'base',
|
|
38
|
+
Name: 'Base Task',
|
|
39
|
+
Description: 'Override this in subclasses.',
|
|
40
|
+
Category: 'internal',
|
|
41
|
+
EventInputs: [],
|
|
42
|
+
EventOutputs: [],
|
|
43
|
+
SettingsInputs: [],
|
|
44
|
+
StateOutputs: [],
|
|
45
|
+
DefaultSettings: {}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Execute the task.
|
|
51
|
+
*
|
|
52
|
+
* @param {object} pResolvedSettings - The task's Settings with incoming
|
|
53
|
+
* State connections already resolved by the engine.
|
|
54
|
+
* @param {object} pExecutionContext - Runtime context:
|
|
55
|
+
* GlobalState {object} - state shared across all operations
|
|
56
|
+
* OperationState {object} - state shared across tasks in this run
|
|
57
|
+
* TaskOutputs {object} - keyed by NodeHash, per-task output state
|
|
58
|
+
* StagingPath {string} - path to the operation's staging folder
|
|
59
|
+
* OperationHash {string} - the operation being executed
|
|
60
|
+
* NodeHash {string} - this task node's hash in the graph
|
|
61
|
+
* RunHash {string} - the execution run hash
|
|
62
|
+
* RunMode {string} - 'production' | 'standard' | 'debug'
|
|
63
|
+
* StateManager {object} - reference to the StateManager service
|
|
64
|
+
* TriggeringEventName {string} - the event that triggered this execution
|
|
65
|
+
* (e.g. 'PerformSplit', 'StepComplete') — lets state-machine tasks
|
|
66
|
+
* distinguish which input event caused the current invocation.
|
|
67
|
+
* @param {function} fCallback - function(pError, pResult) where pResult is:
|
|
68
|
+
* EventToFire {string} - which output event to fire (e.g. 'ReadComplete')
|
|
69
|
+
* Outputs {object} - key/value pairs written to TaskOutputs[NodeHash]
|
|
70
|
+
* Log {Array} - array of log message strings
|
|
71
|
+
* @param {function} [fFireIntermediateEvent] - Optional. For re-entrant
|
|
72
|
+
* tasks (like split-execute) that fire events multiple times.
|
|
73
|
+
* Signature: fFireIntermediateEvent(pEventName, pOutputs, fResumeCallback)
|
|
74
|
+
*/
|
|
75
|
+
execute(pResolvedSettings, pExecutionContext, fCallback, fFireIntermediateEvent)
|
|
76
|
+
{
|
|
77
|
+
return fCallback(new Error(`Task type "${this.definition.Hash}" has not implemented execute().`));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Resolve a file path relative to the staging folder if it is not absolute.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} pFilePath - The file path from settings.
|
|
84
|
+
* @param {string} pStagingPath - The operation's staging folder.
|
|
85
|
+
* @returns {string} Resolved absolute path.
|
|
86
|
+
*/
|
|
87
|
+
resolveFilePath(pFilePath, pStagingPath)
|
|
88
|
+
{
|
|
89
|
+
if (!pFilePath || typeof(pFilePath) !== 'string')
|
|
90
|
+
{
|
|
91
|
+
return pStagingPath || '';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const libPath = require('path');
|
|
95
|
+
|
|
96
|
+
if (libPath.isAbsolute(pFilePath))
|
|
97
|
+
{
|
|
98
|
+
return pFilePath;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return libPath.resolve(pStagingPath || process.cwd(), pFilePath);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = UltravisorTaskType;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
const libTaskTypeBase = require('../Ultravisor-TaskType-Base.cjs');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* IfConditional Task Type
|
|
5
|
+
*
|
|
6
|
+
* Evaluates a simple expression and fires either the True or False event.
|
|
7
|
+
* Supports comparisons via DataAddress and CompareValue, or a direct Expression string.
|
|
8
|
+
*
|
|
9
|
+
* If DataAddress is set, reads the value at that address and compares
|
|
10
|
+
* it to CompareValue using the Operator (default: '==').
|
|
11
|
+
*
|
|
12
|
+
* If Expression is set instead, evaluates it using fable.ExpressionParser.
|
|
13
|
+
*/
|
|
14
|
+
class UltravisorTaskTypeIfConditional extends libTaskTypeBase
|
|
15
|
+
{
|
|
16
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
17
|
+
{
|
|
18
|
+
super(pFable, pOptions, pServiceHash);
|
|
19
|
+
|
|
20
|
+
this.serviceType = 'UltravisorTaskTypeIfConditional';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get definition()
|
|
24
|
+
{
|
|
25
|
+
return {
|
|
26
|
+
Hash: 'if-conditional',
|
|
27
|
+
Type: 'if-conditional',
|
|
28
|
+
Name: 'If Conditional',
|
|
29
|
+
Description: 'Evaluates a condition and branches execution to True or False.',
|
|
30
|
+
Category: 'control',
|
|
31
|
+
|
|
32
|
+
EventInputs: [{ Name: 'Evaluate' }],
|
|
33
|
+
EventOutputs: [
|
|
34
|
+
{ Name: 'True' },
|
|
35
|
+
{ Name: 'False' }
|
|
36
|
+
],
|
|
37
|
+
SettingsInputs: [
|
|
38
|
+
{ Name: 'DataAddress', DataType: 'String', Required: false },
|
|
39
|
+
{ Name: 'CompareValue', DataType: 'String', Required: false },
|
|
40
|
+
{ Name: 'Operator', DataType: 'String', Required: false },
|
|
41
|
+
{ Name: 'Expression', DataType: 'String', Required: false }
|
|
42
|
+
],
|
|
43
|
+
StateOutputs: [
|
|
44
|
+
{ Name: 'Result', DataType: 'Boolean' }
|
|
45
|
+
],
|
|
46
|
+
|
|
47
|
+
DefaultSettings: { DataAddress: '', CompareValue: '', Operator: '==', Expression: '' }
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
execute(pResolvedSettings, pExecutionContext, fCallback, fFireIntermediateEvent)
|
|
52
|
+
{
|
|
53
|
+
let tmpResult = false;
|
|
54
|
+
|
|
55
|
+
if (pResolvedSettings.Expression && typeof(pResolvedSettings.Expression) === 'string')
|
|
56
|
+
{
|
|
57
|
+
// Expression-based evaluation using Fable's ExpressionParser
|
|
58
|
+
try
|
|
59
|
+
{
|
|
60
|
+
if (this.fable.ExpressionParser)
|
|
61
|
+
{
|
|
62
|
+
tmpResult = this.fable.ExpressionParser.resolve(pResolvedSettings.Expression, pExecutionContext);
|
|
63
|
+
}
|
|
64
|
+
else
|
|
65
|
+
{
|
|
66
|
+
// Simple truthy evaluation fallback
|
|
67
|
+
tmpResult = !!pResolvedSettings.Expression;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch (pError)
|
|
71
|
+
{
|
|
72
|
+
return fCallback(null, {
|
|
73
|
+
EventToFire: 'False',
|
|
74
|
+
Outputs: { Result: false },
|
|
75
|
+
Log: [`Expression error: ${pError.message}`]
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else if (pResolvedSettings.DataAddress)
|
|
80
|
+
{
|
|
81
|
+
// Address-based comparison
|
|
82
|
+
let tmpDataValue = undefined;
|
|
83
|
+
let tmpStateManager = pExecutionContext.StateManager;
|
|
84
|
+
|
|
85
|
+
if (tmpStateManager)
|
|
86
|
+
{
|
|
87
|
+
tmpDataValue = tmpStateManager.resolveAddress(
|
|
88
|
+
pResolvedSettings.DataAddress, pExecutionContext, pExecutionContext.NodeHash);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let tmpCompareValue = pResolvedSettings.CompareValue;
|
|
92
|
+
let tmpOperator = pResolvedSettings.Operator || '==';
|
|
93
|
+
|
|
94
|
+
tmpResult = this._compare(tmpDataValue, tmpCompareValue, tmpOperator);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return fCallback(null, {
|
|
98
|
+
EventToFire: tmpResult ? 'True' : 'False',
|
|
99
|
+
Outputs: { Result: tmpResult },
|
|
100
|
+
Log: [`Condition evaluated to ${tmpResult}.`]
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Compare two values using an operator.
|
|
106
|
+
*
|
|
107
|
+
* @param {*} pLeft - Left operand.
|
|
108
|
+
* @param {*} pRight - Right operand.
|
|
109
|
+
* @param {string} pOperator - The comparison operator.
|
|
110
|
+
* @returns {boolean} Result of comparison.
|
|
111
|
+
*/
|
|
112
|
+
_compare(pLeft, pRight, pOperator)
|
|
113
|
+
{
|
|
114
|
+
switch (pOperator)
|
|
115
|
+
{
|
|
116
|
+
case '==':
|
|
117
|
+
return String(pLeft) == String(pRight);
|
|
118
|
+
case '===':
|
|
119
|
+
return pLeft === pRight;
|
|
120
|
+
case '!=':
|
|
121
|
+
return String(pLeft) != String(pRight);
|
|
122
|
+
case '!==':
|
|
123
|
+
return pLeft !== pRight;
|
|
124
|
+
case '>':
|
|
125
|
+
return Number(pLeft) > Number(pRight);
|
|
126
|
+
case '<':
|
|
127
|
+
return Number(pLeft) < Number(pRight);
|
|
128
|
+
case '>=':
|
|
129
|
+
return Number(pLeft) >= Number(pRight);
|
|
130
|
+
case '<=':
|
|
131
|
+
return Number(pLeft) <= Number(pRight);
|
|
132
|
+
case 'contains':
|
|
133
|
+
return String(pLeft).indexOf(String(pRight)) > -1;
|
|
134
|
+
case 'startsWith':
|
|
135
|
+
return String(pLeft).startsWith(String(pRight));
|
|
136
|
+
case 'endsWith':
|
|
137
|
+
return String(pLeft).endsWith(String(pRight));
|
|
138
|
+
case 'truthy':
|
|
139
|
+
return !!pLeft;
|
|
140
|
+
case 'falsy':
|
|
141
|
+
return !pLeft;
|
|
142
|
+
default:
|
|
143
|
+
return String(pLeft) == String(pRight);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
module.exports = UltravisorTaskTypeIfConditional;
|