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
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
const libUltravisorEventBase = require(`../Ultravisor-Hypervisor-Event-Base.cjs`);
|
|
2
|
-
|
|
3
|
-
class UltravisorEventSolver extends libUltravisorEventBase
|
|
4
|
-
{
|
|
5
|
-
constructor(pPict, pOptions, pServiceHash)
|
|
6
|
-
{
|
|
7
|
-
super(pPict, pOptions, pServiceHash);
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
module.exports = UltravisorEventSolver;
|
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
const libFS = require('fs');
|
|
2
|
-
const libPath = require('path');
|
|
3
|
-
|
|
4
|
-
class UltravisorTaskBase
|
|
5
|
-
{
|
|
6
|
-
constructor(pFable)
|
|
7
|
-
{
|
|
8
|
-
this.fable = pFable;
|
|
9
|
-
this.log = pFable.log;
|
|
10
|
-
|
|
11
|
-
this._Manyfest = this.fable.newManyfest();
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Execute the task type logic.
|
|
16
|
-
* Subclasses must override this method.
|
|
17
|
-
*/
|
|
18
|
-
execute(pTaskDefinition, pContext, pManifestEntry, fCallback)
|
|
19
|
-
{
|
|
20
|
-
pManifestEntry.StopTime = new Date().toISOString();
|
|
21
|
-
pManifestEntry.Status = 'Unsupported';
|
|
22
|
-
pManifestEntry.Log.push(`Task type not implemented in base class.`);
|
|
23
|
-
return fCallback(null, pManifestEntry);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Resolve the staging folder path.
|
|
28
|
-
*
|
|
29
|
-
* Priority:
|
|
30
|
-
* 1. pContext.StagingPath (per-operation override)
|
|
31
|
-
* 2. UltravisorFileStorePath from ProgramConfiguration
|
|
32
|
-
* 3. ${cwd}/dist/ultravisor_datastore (fallback)
|
|
33
|
-
*
|
|
34
|
-
* @param {object} pContext - Execution context.
|
|
35
|
-
* @returns {string} Absolute path to the staging folder.
|
|
36
|
-
*/
|
|
37
|
-
resolveStagingPath(pContext)
|
|
38
|
-
{
|
|
39
|
-
if (pContext && pContext.StagingPath && pContext.StagingPath.length > 0)
|
|
40
|
-
{
|
|
41
|
-
return pContext.StagingPath;
|
|
42
|
-
}
|
|
43
|
-
return (this.fable?.ProgramConfiguration?.UltravisorFileStorePath)
|
|
44
|
-
|| `${process.cwd()}/dist/ultravisor_datastore`;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Build a full file path inside the staging folder.
|
|
49
|
-
* Prevents path traversal by rejecting paths containing "..".
|
|
50
|
-
*
|
|
51
|
-
* @param {string} pStagingPath - Base staging folder.
|
|
52
|
-
* @param {string} pFileName - Relative file name or path.
|
|
53
|
-
* @returns {string|false} Full path or false if invalid.
|
|
54
|
-
*/
|
|
55
|
-
resolveStagingFilePath(pStagingPath, pFileName)
|
|
56
|
-
{
|
|
57
|
-
if (!pFileName || typeof(pFileName) !== 'string' || pFileName.length === 0)
|
|
58
|
-
{
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
if (pFileName.indexOf('..') !== -1)
|
|
62
|
-
{
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
return libPath.resolve(pStagingPath, pFileName);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Resolve a dot-notation address from the execution context.
|
|
70
|
-
*
|
|
71
|
-
* Looks up the address in:
|
|
72
|
-
* 1. pContext.GlobalState
|
|
73
|
-
* 2. pContext.NodeState
|
|
74
|
-
*
|
|
75
|
-
* @param {string} pAddress - Dot-notation path (e.g. "Flags.Enabled").
|
|
76
|
-
* @param {object} pContext - Execution context.
|
|
77
|
-
* @returns {*} The resolved value, or undefined if not found.
|
|
78
|
-
*/
|
|
79
|
-
resolveAddress(pAddress, pContext)
|
|
80
|
-
{
|
|
81
|
-
if (!pAddress || !pContext)
|
|
82
|
-
{
|
|
83
|
-
return undefined;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
let tmpParts = pAddress.split('.');
|
|
87
|
-
|
|
88
|
-
// Try GlobalState first
|
|
89
|
-
let tmpValue = this.walkObject(pContext.GlobalState, tmpParts);
|
|
90
|
-
if (tmpValue !== undefined)
|
|
91
|
-
{
|
|
92
|
-
return tmpValue;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Fall back to NodeState
|
|
96
|
-
return this.walkObject(pContext.NodeState, tmpParts);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Walk an object by a path array.
|
|
101
|
-
*
|
|
102
|
-
* @param {object} pObject - Object to walk.
|
|
103
|
-
* @param {array} pParts - Array of keys.
|
|
104
|
-
* @returns {*} Value at the path, or undefined.
|
|
105
|
-
*/
|
|
106
|
-
walkObject(pObject, pParts)
|
|
107
|
-
{
|
|
108
|
-
if (!pObject || typeof(pObject) !== 'object')
|
|
109
|
-
{
|
|
110
|
-
return undefined;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
let tmpCurrent = pObject;
|
|
114
|
-
for (let i = 0; i < pParts.length; i++)
|
|
115
|
-
{
|
|
116
|
-
if (tmpCurrent === null || tmpCurrent === undefined || typeof(tmpCurrent) !== 'object')
|
|
117
|
-
{
|
|
118
|
-
return undefined;
|
|
119
|
-
}
|
|
120
|
-
tmpCurrent = tmpCurrent[pParts[i]];
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return tmpCurrent;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// =========================================================================
|
|
127
|
-
// Destination -- store task output at a manyfest address in GlobalState
|
|
128
|
-
// =========================================================================
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Store the task output at a manyfest address in pContext.GlobalState.
|
|
132
|
-
*
|
|
133
|
-
* The address is taken from pTaskDefinition.Destination. If Destination
|
|
134
|
-
* is not set, the data is stored at "Output" (the default address).
|
|
135
|
-
*
|
|
136
|
-
* This provides a consistent way for all Read and Get task types to
|
|
137
|
-
* declare where their data goes in the operation's shared state.
|
|
138
|
-
*
|
|
139
|
-
* @param {object} pTaskDefinition - The task definition (checked for .Destination).
|
|
140
|
-
* @param {object} pContext - Execution context (GlobalState).
|
|
141
|
-
* @param {object} pManifestEntry - The manifest entry (for logging).
|
|
142
|
-
* @param {*} pData - The data to store.
|
|
143
|
-
*/
|
|
144
|
-
storeDestination(pTaskDefinition, pContext, pManifestEntry, pData)
|
|
145
|
-
{
|
|
146
|
-
let tmpDestination = (pTaskDefinition.Destination && typeof(pTaskDefinition.Destination) === 'string' && pTaskDefinition.Destination.length > 0)
|
|
147
|
-
? pTaskDefinition.Destination
|
|
148
|
-
: 'Output';
|
|
149
|
-
|
|
150
|
-
if (!pContext.GlobalState || typeof(pContext.GlobalState) !== 'object')
|
|
151
|
-
{
|
|
152
|
-
pContext.GlobalState = {};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
this._Manyfest.setValueAtAddress(pContext.GlobalState, tmpDestination, pData);
|
|
156
|
-
pManifestEntry.Log.push(`Destination: stored result at "${tmpDestination}".`);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// =========================================================================
|
|
160
|
-
// Persist -- store task output to state address or staging file
|
|
161
|
-
// =========================================================================
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Persist task output according to the Persist parameter on the task
|
|
165
|
-
* definition.
|
|
166
|
-
*
|
|
167
|
-
* Persist can be:
|
|
168
|
-
* - A manyfest address string (dot-notation) -- stores pData into
|
|
169
|
-
* pContext.GlobalState at that address.
|
|
170
|
-
* - An object with:
|
|
171
|
-
* { "Address": "Some.State.Path" } -- same as the string form
|
|
172
|
-
* { "File": "relative/path.json" } -- writes pData as JSON to
|
|
173
|
-
* a file relative to the staging folder
|
|
174
|
-
* { "File": "relative/path.bin" } -- writes pData as a Buffer
|
|
175
|
-
* to a file relative to the staging folder (for binary data)
|
|
176
|
-
*
|
|
177
|
-
* @param {object} pTaskDefinition - The task definition (checked for .Persist).
|
|
178
|
-
* @param {object} pContext - Execution context (GlobalState, StagingPath).
|
|
179
|
-
* @param {object} pManifestEntry - The manifest entry (for logging).
|
|
180
|
-
* @param {*} pData - The data to persist (string, object, or Buffer).
|
|
181
|
-
*/
|
|
182
|
-
storeResult(pTaskDefinition, pContext, pManifestEntry, pData)
|
|
183
|
-
{
|
|
184
|
-
if (!pTaskDefinition.Persist)
|
|
185
|
-
{
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
let tmpPersist = pTaskDefinition.Persist;
|
|
190
|
-
|
|
191
|
-
// String form -- treat as a manyfest address into GlobalState
|
|
192
|
-
if (typeof(tmpPersist) === 'string' && tmpPersist.length > 0)
|
|
193
|
-
{
|
|
194
|
-
if (!pContext.GlobalState || typeof(pContext.GlobalState) !== 'object')
|
|
195
|
-
{
|
|
196
|
-
pContext.GlobalState = {};
|
|
197
|
-
}
|
|
198
|
-
this._Manyfest.setValueAtAddress(pContext.GlobalState, tmpPersist, pData);
|
|
199
|
-
pManifestEntry.Log.push(`Persist: stored result at state address "${tmpPersist}".`);
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (typeof(tmpPersist) !== 'object' || tmpPersist === null)
|
|
204
|
-
{
|
|
205
|
-
pManifestEntry.Log.push(`Persist: invalid Persist value (expected string or object).`);
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Object form with Address -- store in GlobalState
|
|
210
|
-
if (tmpPersist.Address && typeof(tmpPersist.Address) === 'string')
|
|
211
|
-
{
|
|
212
|
-
if (!pContext.GlobalState || typeof(pContext.GlobalState) !== 'object')
|
|
213
|
-
{
|
|
214
|
-
pContext.GlobalState = {};
|
|
215
|
-
}
|
|
216
|
-
this._Manyfest.setValueAtAddress(pContext.GlobalState, tmpPersist.Address, pData);
|
|
217
|
-
pManifestEntry.Log.push(`Persist: stored result at state address "${tmpPersist.Address}".`);
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Object form with File -- write to staging folder
|
|
222
|
-
if (tmpPersist.File && typeof(tmpPersist.File) === 'string')
|
|
223
|
-
{
|
|
224
|
-
let tmpStagingPath = this.resolveStagingPath(pContext);
|
|
225
|
-
let tmpFilePath = this.resolveStagingFilePath(tmpStagingPath, tmpPersist.File);
|
|
226
|
-
|
|
227
|
-
if (!tmpFilePath)
|
|
228
|
-
{
|
|
229
|
-
pManifestEntry.Log.push(`Persist: invalid file path "${tmpPersist.File}".`);
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
try
|
|
234
|
-
{
|
|
235
|
-
let tmpDir = libPath.dirname(tmpFilePath);
|
|
236
|
-
if (!libFS.existsSync(tmpDir))
|
|
237
|
-
{
|
|
238
|
-
libFS.mkdirSync(tmpDir, { recursive: true });
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (Buffer.isBuffer(pData))
|
|
242
|
-
{
|
|
243
|
-
libFS.writeFileSync(tmpFilePath, pData);
|
|
244
|
-
pManifestEntry.Log.push(`Persist: wrote ${pData.length} bytes (binary) to "${tmpPersist.File}".`);
|
|
245
|
-
}
|
|
246
|
-
else
|
|
247
|
-
{
|
|
248
|
-
let tmpContent = (typeof(pData) === 'string') ? pData : JSON.stringify(pData, null, 4);
|
|
249
|
-
libFS.writeFileSync(tmpFilePath, tmpContent, 'utf8');
|
|
250
|
-
pManifestEntry.Log.push(`Persist: wrote ${tmpContent.length} bytes to "${tmpPersist.File}".`);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
catch (pError)
|
|
254
|
-
{
|
|
255
|
-
pManifestEntry.Log.push(`Persist: error writing to file: ${pError.message}`);
|
|
256
|
-
}
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
pManifestEntry.Log.push(`Persist: object must have an "Address" or "File" property.`);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
module.exports = UltravisorTaskBase;
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
const libUltravisorTaskBase = require('./Ultravisor-Task-Base.cjs');
|
|
2
|
-
|
|
3
|
-
class UltravisorTaskCollectValues extends libUltravisorTaskBase
|
|
4
|
-
{
|
|
5
|
-
constructor(pFable)
|
|
6
|
-
{
|
|
7
|
-
super(pFable);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Collect and extract field values from arrays in GlobalState.
|
|
12
|
-
*
|
|
13
|
-
* Designed to work with GeneratePagedOperation output but is
|
|
14
|
-
* general-purpose. Iterates over a source array, optionally
|
|
15
|
-
* navigates into each element via RecordPath, then plucks the
|
|
16
|
-
* specified Field from every record found.
|
|
17
|
-
*
|
|
18
|
-
* Task definition fields:
|
|
19
|
-
* - Address: manyfest address in GlobalState pointing to the
|
|
20
|
-
* source array (e.g. "Pages").
|
|
21
|
-
* - Field: the field name to extract from each record. Supports
|
|
22
|
-
* dot-notation for nested fields (e.g. "Details.ID").
|
|
23
|
-
* - RecordPath (optional): dot-notation path within each element
|
|
24
|
-
* to reach the array of records (e.g. "JSON.records" for
|
|
25
|
-
* RestRequest results). If omitted, each element is treated
|
|
26
|
-
* as the array itself (or as a single record if it is an
|
|
27
|
-
* object).
|
|
28
|
-
* - Unique (optional, default false): if true, deduplicate the
|
|
29
|
-
* output array.
|
|
30
|
-
* - Flatten (optional, default true): if true, flatten nested
|
|
31
|
-
* arrays into a single flat array.
|
|
32
|
-
* - Destination (optional): manyfest address in GlobalState
|
|
33
|
-
* where the result array is stored (default: "Output").
|
|
34
|
-
* - Persist (optional): standard persist options (file or address).
|
|
35
|
-
*/
|
|
36
|
-
execute(pTaskDefinition, pContext, pManifestEntry, fCallback)
|
|
37
|
-
{
|
|
38
|
-
// --- Validate required fields ---
|
|
39
|
-
if (!pTaskDefinition.Address || typeof(pTaskDefinition.Address) !== 'string' || pTaskDefinition.Address.length === 0)
|
|
40
|
-
{
|
|
41
|
-
pManifestEntry.StopTime = new Date().toISOString();
|
|
42
|
-
pManifestEntry.Status = 'Error';
|
|
43
|
-
pManifestEntry.Log.push(`CollectValues: missing or empty Address field.`);
|
|
44
|
-
return fCallback(null, pManifestEntry);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (!pTaskDefinition.Field || typeof(pTaskDefinition.Field) !== 'string' || pTaskDefinition.Field.length === 0)
|
|
48
|
-
{
|
|
49
|
-
pManifestEntry.StopTime = new Date().toISOString();
|
|
50
|
-
pManifestEntry.Status = 'Error';
|
|
51
|
-
pManifestEntry.Log.push(`CollectValues: missing or empty Field field.`);
|
|
52
|
-
return fCallback(null, pManifestEntry);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// --- Resolve source data ---
|
|
56
|
-
let tmpSource = this.resolveAddress(pTaskDefinition.Address, pContext);
|
|
57
|
-
|
|
58
|
-
if (tmpSource === undefined || tmpSource === null)
|
|
59
|
-
{
|
|
60
|
-
pManifestEntry.StopTime = new Date().toISOString();
|
|
61
|
-
pManifestEntry.Status = 'Complete';
|
|
62
|
-
pManifestEntry.Success = true;
|
|
63
|
-
pManifestEntry.Output = '[]';
|
|
64
|
-
pManifestEntry.Log.push(`CollectValues: Address "${pTaskDefinition.Address}" resolved to null or undefined; treating as empty set.`);
|
|
65
|
-
this.storeDestination(pTaskDefinition, pContext, pManifestEntry, []);
|
|
66
|
-
return fCallback(null, pManifestEntry);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// If the source is not an array, wrap it so we can iterate uniformly
|
|
70
|
-
if (!Array.isArray(tmpSource))
|
|
71
|
-
{
|
|
72
|
-
if (typeof(tmpSource) === 'object')
|
|
73
|
-
{
|
|
74
|
-
tmpSource = [tmpSource];
|
|
75
|
-
pManifestEntry.Log.push(`CollectValues: source is a single object; wrapped in array.`);
|
|
76
|
-
}
|
|
77
|
-
else
|
|
78
|
-
{
|
|
79
|
-
pManifestEntry.StopTime = new Date().toISOString();
|
|
80
|
-
pManifestEntry.Status = 'Error';
|
|
81
|
-
pManifestEntry.Log.push(`CollectValues: Address "${pTaskDefinition.Address}" did not resolve to an array or object.`);
|
|
82
|
-
return fCallback(null, pManifestEntry);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
pManifestEntry.Log.push(`CollectValues: source has ${tmpSource.length} element(s).`);
|
|
87
|
-
|
|
88
|
-
let tmpFieldParts = pTaskDefinition.Field.split('.');
|
|
89
|
-
let tmpRecordPathParts = (pTaskDefinition.RecordPath && typeof(pTaskDefinition.RecordPath) === 'string' && pTaskDefinition.RecordPath.length > 0)
|
|
90
|
-
? pTaskDefinition.RecordPath.split('.')
|
|
91
|
-
: null;
|
|
92
|
-
|
|
93
|
-
let tmpFlatten = (pTaskDefinition.Flatten !== false);
|
|
94
|
-
let tmpUnique = (pTaskDefinition.Unique === true);
|
|
95
|
-
|
|
96
|
-
// --- Iterate over each element in the source ---
|
|
97
|
-
let tmpCollected = [];
|
|
98
|
-
|
|
99
|
-
for (let i = 0; i < tmpSource.length; i++)
|
|
100
|
-
{
|
|
101
|
-
let tmpElement = tmpSource[i];
|
|
102
|
-
|
|
103
|
-
if (tmpElement === null || tmpElement === undefined)
|
|
104
|
-
{
|
|
105
|
-
pManifestEntry.Log.push(`CollectValues: skipping null/undefined element at index ${i}.`);
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// If RecordPath is set, navigate into the element to find the records
|
|
110
|
-
let tmpRecords = tmpElement;
|
|
111
|
-
|
|
112
|
-
if (tmpRecordPathParts)
|
|
113
|
-
{
|
|
114
|
-
tmpRecords = this.walkObject(tmpElement, tmpRecordPathParts);
|
|
115
|
-
|
|
116
|
-
if (tmpRecords === undefined || tmpRecords === null)
|
|
117
|
-
{
|
|
118
|
-
pManifestEntry.Log.push(`CollectValues: RecordPath "${pTaskDefinition.RecordPath}" resolved to null at index ${i}, skipping.`);
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Normalize to an array of records
|
|
124
|
-
if (!Array.isArray(tmpRecords))
|
|
125
|
-
{
|
|
126
|
-
if (typeof(tmpRecords) === 'object')
|
|
127
|
-
{
|
|
128
|
-
tmpRecords = [tmpRecords];
|
|
129
|
-
}
|
|
130
|
-
else
|
|
131
|
-
{
|
|
132
|
-
pManifestEntry.Log.push(`CollectValues: element at index ${i} is not an array or object after RecordPath, skipping.`);
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Pluck the field from each record
|
|
138
|
-
for (let j = 0; j < tmpRecords.length; j++)
|
|
139
|
-
{
|
|
140
|
-
let tmpRecord = tmpRecords[j];
|
|
141
|
-
|
|
142
|
-
if (tmpRecord === null || tmpRecord === undefined || typeof(tmpRecord) !== 'object')
|
|
143
|
-
{
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
let tmpValue = this.walkObject(tmpRecord, tmpFieldParts);
|
|
148
|
-
|
|
149
|
-
if (tmpValue !== undefined)
|
|
150
|
-
{
|
|
151
|
-
tmpCollected.push(tmpValue);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
pManifestEntry.Log.push(`CollectValues: collected ${tmpCollected.length} value(s) from field "${pTaskDefinition.Field}".`);
|
|
157
|
-
|
|
158
|
-
// --- Flatten ---
|
|
159
|
-
if (tmpFlatten)
|
|
160
|
-
{
|
|
161
|
-
tmpCollected = tmpCollected.flat(Infinity);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// --- Deduplicate ---
|
|
165
|
-
if (tmpUnique)
|
|
166
|
-
{
|
|
167
|
-
let tmpBefore = tmpCollected.length;
|
|
168
|
-
tmpCollected = [...new Set(tmpCollected)];
|
|
169
|
-
if (tmpCollected.length < tmpBefore)
|
|
170
|
-
{
|
|
171
|
-
pManifestEntry.Log.push(`CollectValues: deduplicated from ${tmpBefore} to ${tmpCollected.length} value(s).`);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// --- Store results ---
|
|
176
|
-
pManifestEntry.StopTime = new Date().toISOString();
|
|
177
|
-
pManifestEntry.Status = 'Complete';
|
|
178
|
-
pManifestEntry.Success = true;
|
|
179
|
-
pManifestEntry.Output = JSON.stringify(tmpCollected);
|
|
180
|
-
|
|
181
|
-
this.storeDestination(pTaskDefinition, pContext, pManifestEntry, tmpCollected);
|
|
182
|
-
this.storeResult(pTaskDefinition, pContext, pManifestEntry, tmpCollected);
|
|
183
|
-
|
|
184
|
-
return fCallback(null, pManifestEntry);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
module.exports = UltravisorTaskCollectValues;
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
const libUltravisorTaskBase = require('./Ultravisor-Task-Base.cjs');
|
|
2
|
-
|
|
3
|
-
const libChildProcess = require('child_process');
|
|
4
|
-
|
|
5
|
-
class UltravisorTaskCommand extends libUltravisorTaskBase
|
|
6
|
-
{
|
|
7
|
-
constructor(pFable)
|
|
8
|
-
{
|
|
9
|
-
super(pFable);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Execute a shell command task.
|
|
14
|
-
*/
|
|
15
|
-
execute(pTaskDefinition, pContext, pManifestEntry, fCallback)
|
|
16
|
-
{
|
|
17
|
-
let tmpCommand = pTaskDefinition.Command || pTaskDefinition.Parameters || '';
|
|
18
|
-
|
|
19
|
-
if (!tmpCommand || tmpCommand.length === 0)
|
|
20
|
-
{
|
|
21
|
-
pManifestEntry.StopTime = new Date().toISOString();
|
|
22
|
-
pManifestEntry.Status = 'Error';
|
|
23
|
-
pManifestEntry.Log.push(`Command task has no command to execute.`);
|
|
24
|
-
return fCallback(null, pManifestEntry);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
pManifestEntry.Log.push(`Executing command: ${tmpCommand}`);
|
|
28
|
-
|
|
29
|
-
let tmpTimeout = (this.fable?.ProgramConfiguration?.UltravisorCommandTimeoutMilliseconds) || 300000;
|
|
30
|
-
let tmpMaxBuffer = (this.fable?.ProgramConfiguration?.UltravisorCommandMaxBufferBytes) || 10485760;
|
|
31
|
-
|
|
32
|
-
libChildProcess.exec(tmpCommand, { timeout: tmpTimeout, maxBuffer: tmpMaxBuffer },
|
|
33
|
-
(pError, pStdOut, pStdErr) =>
|
|
34
|
-
{
|
|
35
|
-
pManifestEntry.StopTime = new Date().toISOString();
|
|
36
|
-
|
|
37
|
-
if (pStdOut)
|
|
38
|
-
{
|
|
39
|
-
pManifestEntry.Output = pStdOut;
|
|
40
|
-
pManifestEntry.Log.push(`stdout: ${pStdOut.substring(0, 500)}`);
|
|
41
|
-
}
|
|
42
|
-
if (pStdErr)
|
|
43
|
-
{
|
|
44
|
-
pManifestEntry.Log.push(`stderr: ${pStdErr.substring(0, 500)}`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (pError)
|
|
48
|
-
{
|
|
49
|
-
pManifestEntry.Status = 'Error';
|
|
50
|
-
pManifestEntry.Success = false;
|
|
51
|
-
pManifestEntry.Log.push(`Command failed: ${pError.message}`);
|
|
52
|
-
}
|
|
53
|
-
else
|
|
54
|
-
{
|
|
55
|
-
pManifestEntry.Status = 'Complete';
|
|
56
|
-
pManifestEntry.Success = true;
|
|
57
|
-
pManifestEntry.Log.push(`Command completed successfully.`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return fCallback(null, pManifestEntry);
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
module.exports = UltravisorTaskCommand;
|