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.
Files changed (121) hide show
  1. package/.claude/launch.json +11 -0
  2. package/.claude/ultravisor-dev-config.json +3 -0
  3. package/.ultravisor.json +426 -0
  4. package/docs/README.md +63 -0
  5. package/package.json +12 -8
  6. package/source/Ultravisor.cjs +22 -3
  7. package/source/cli/Ultravisor-CLIProgram.cjs +35 -23
  8. package/source/cli/commands/Ultravisor-Command-SingleOperation.cjs +29 -18
  9. package/source/cli/commands/Ultravisor-Command-SingleTask.cjs +62 -19
  10. package/source/cli/commands/Ultravisor-Command-UpdateTask.cjs +27 -15
  11. package/source/config/Ultravisor-Default-Command-Configuration.cjs +5 -3
  12. package/source/services/Ultravisor-ExecutionEngine.cjs +1039 -0
  13. package/source/services/Ultravisor-ExecutionManifest.cjs +399 -0
  14. package/source/services/Ultravisor-Hypervisor-State.cjs +270 -97
  15. package/source/services/Ultravisor-Hypervisor.cjs +38 -83
  16. package/source/services/Ultravisor-StateManager.cjs +241 -0
  17. package/source/services/Ultravisor-TaskTypeRegistry.cjs +143 -0
  18. package/source/services/tasks/Ultravisor-TaskType-Base.cjs +105 -0
  19. package/source/services/tasks/control/Ultravisor-TaskType-IfConditional.cjs +148 -0
  20. package/source/services/tasks/control/Ultravisor-TaskType-LaunchOperation.cjs +187 -0
  21. package/source/services/tasks/control/Ultravisor-TaskType-SplitExecute.cjs +184 -0
  22. package/source/services/tasks/data/Ultravisor-TaskType-ReplaceString.cjs +82 -0
  23. package/source/services/tasks/data/Ultravisor-TaskType-SetValues.cjs +81 -0
  24. package/source/services/tasks/data/Ultravisor-TaskType-StringAppender.cjs +101 -0
  25. package/source/services/tasks/file-io/Ultravisor-TaskType-ReadFile.cjs +103 -0
  26. package/source/services/tasks/file-io/Ultravisor-TaskType-WriteFile.cjs +117 -0
  27. package/source/services/tasks/interaction/Ultravisor-TaskType-ErrorMessage.cjs +54 -0
  28. package/source/services/tasks/interaction/Ultravisor-TaskType-ValueInput.cjs +62 -0
  29. package/source/web_server/Ultravisor-API-Server.cjs +237 -124
  30. package/test/Ultravisor_browser_tests.js +2226 -0
  31. package/test/Ultravisor_tests.js +1143 -5830
  32. package/webinterface/css/ultravisor.css +23 -0
  33. package/webinterface/package.json +6 -3
  34. package/webinterface/source/Pict-Application-Ultravisor.js +93 -73
  35. package/webinterface/source/cards/FlowCard-CSVTransform.js +43 -0
  36. package/webinterface/source/cards/FlowCard-Command.js +86 -0
  37. package/webinterface/source/cards/FlowCard-ComprehensionIntersect.js +40 -0
  38. package/webinterface/source/cards/FlowCard-Conditional.js +87 -0
  39. package/webinterface/source/cards/FlowCard-CopyFile.js +55 -0
  40. package/webinterface/source/cards/FlowCard-End.js +29 -0
  41. package/webinterface/source/cards/FlowCard-GetJSON.js +55 -0
  42. package/webinterface/source/cards/FlowCard-GetText.js +54 -0
  43. package/webinterface/source/cards/FlowCard-Histogram.js +176 -0
  44. package/webinterface/source/cards/FlowCard-LaunchOperation.js +82 -0
  45. package/webinterface/source/cards/FlowCard-ListFiles.js +55 -0
  46. package/webinterface/source/cards/FlowCard-MeadowCount.js +44 -0
  47. package/webinterface/source/cards/FlowCard-MeadowCreate.js +44 -0
  48. package/webinterface/source/cards/FlowCard-MeadowDelete.js +45 -0
  49. package/webinterface/source/cards/FlowCard-MeadowRead.js +46 -0
  50. package/webinterface/source/cards/FlowCard-MeadowReads.js +46 -0
  51. package/webinterface/source/cards/FlowCard-MeadowUpdate.js +44 -0
  52. package/webinterface/source/cards/FlowCard-ParseCSV.js +85 -0
  53. package/webinterface/source/cards/FlowCard-ReadJSON.js +54 -0
  54. package/webinterface/source/cards/FlowCard-ReadText.js +54 -0
  55. package/webinterface/source/cards/FlowCard-RestRequest.js +59 -0
  56. package/webinterface/source/cards/FlowCard-SendJSON.js +57 -0
  57. package/webinterface/source/cards/FlowCard-Solver.js +77 -0
  58. package/webinterface/source/cards/FlowCard-Start.js +29 -0
  59. package/webinterface/source/cards/FlowCard-TemplateString.js +77 -0
  60. package/webinterface/source/cards/FlowCard-WriteJSON.js +54 -0
  61. package/webinterface/source/cards/FlowCard-WriteText.js +54 -0
  62. package/webinterface/source/data/ExampleFlow-CSVPipeline.js +231 -0
  63. package/webinterface/source/data/ExampleFlow-FileProcessor.js +315 -0
  64. package/webinterface/source/data/ExampleFlow-MeadowPipeline.js +328 -0
  65. package/webinterface/source/providers/PictRouter-Ultravisor-Configuration.json +8 -8
  66. package/webinterface/source/views/PictView-Ultravisor-Dashboard.js +6 -6
  67. package/webinterface/source/views/PictView-Ultravisor-FlowEditor.js +436 -0
  68. package/webinterface/source/views/PictView-Ultravisor-ManifestList.js +45 -43
  69. package/webinterface/source/views/PictView-Ultravisor-OperationEdit.js +34 -89
  70. package/webinterface/source/views/PictView-Ultravisor-OperationList.js +128 -13
  71. package/webinterface/source/views/PictView-Ultravisor-PendingInput.js +314 -0
  72. package/webinterface/source/views/PictView-Ultravisor-Schedule.js +18 -53
  73. package/webinterface/source/views/PictView-Ultravisor-TimingView.js +27 -14
  74. package/webinterface/source/views/PictView-Ultravisor-TopBar.js +2 -1
  75. package/.babelrc +0 -6
  76. package/.browserslistrc +0 -1
  77. package/.browserslistrc-BACKUP +0 -1
  78. package/.gulpfile-quackage-config.json +0 -7
  79. package/.gulpfile-quackage.js +0 -2
  80. package/debug/Harness.js +0 -5
  81. package/source/services/Ultravisor-Operation-Manifest.cjs +0 -160
  82. package/source/services/Ultravisor-Operation.cjs +0 -200
  83. package/source/services/Ultravisor-Task.cjs +0 -349
  84. package/source/services/events/Ultravisor-Hypervisor-Event-Solver.cjs +0 -11
  85. package/source/services/tasks/Ultravisor-Task-Base.cjs +0 -264
  86. package/source/services/tasks/Ultravisor-Task-CollectValues.cjs +0 -188
  87. package/source/services/tasks/Ultravisor-Task-Command.cjs +0 -65
  88. package/source/services/tasks/Ultravisor-Task-CommandEach.cjs +0 -190
  89. package/source/services/tasks/Ultravisor-Task-Conditional.cjs +0 -104
  90. package/source/services/tasks/Ultravisor-Task-DateWindow.cjs +0 -72
  91. package/source/services/tasks/Ultravisor-Task-GeneratePagedOperation.cjs +0 -336
  92. package/source/services/tasks/Ultravisor-Task-LaunchOperation.cjs +0 -143
  93. package/source/services/tasks/Ultravisor-Task-LaunchTask.cjs +0 -146
  94. package/source/services/tasks/Ultravisor-Task-LineMatch.cjs +0 -158
  95. package/source/services/tasks/Ultravisor-Task-Request.cjs +0 -56
  96. package/source/services/tasks/Ultravisor-Task-Solver.cjs +0 -89
  97. package/source/services/tasks/Ultravisor-Task-TemplateString.cjs +0 -93
  98. package/source/services/tasks/rest/Ultravisor-Task-GetBinary.cjs +0 -127
  99. package/source/services/tasks/rest/Ultravisor-Task-GetJSON.cjs +0 -119
  100. package/source/services/tasks/rest/Ultravisor-Task-GetText.cjs +0 -109
  101. package/source/services/tasks/rest/Ultravisor-Task-GetXML.cjs +0 -112
  102. package/source/services/tasks/rest/Ultravisor-Task-RestRequest.cjs +0 -499
  103. package/source/services/tasks/rest/Ultravisor-Task-SendJSON.cjs +0 -150
  104. package/source/services/tasks/stagingfiles/Ultravisor-Task-CopyFile.cjs +0 -110
  105. package/source/services/tasks/stagingfiles/Ultravisor-Task-ListFiles.cjs +0 -89
  106. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadBinary.cjs +0 -87
  107. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadJSON.cjs +0 -67
  108. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadText.cjs +0 -66
  109. package/source/services/tasks/stagingfiles/Ultravisor-Task-ReadXML.cjs +0 -69
  110. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteBinary.cjs +0 -95
  111. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteJSON.cjs +0 -96
  112. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteText.cjs +0 -99
  113. package/source/services/tasks/stagingfiles/Ultravisor-Task-WriteXML.cjs +0 -102
  114. package/webinterface/.babelrc +0 -6
  115. package/webinterface/.browserslistrc +0 -1
  116. package/webinterface/.browserslistrc-BACKUP +0 -1
  117. package/webinterface/.gulpfile-quackage-config.json +0 -7
  118. package/webinterface/.gulpfile-quackage.js +0 -2
  119. package/webinterface/source/views/PictView-Ultravisor-TaskEdit.js +0 -220
  120. package/webinterface/source/views/PictView-Ultravisor-TaskList.js +0 -248
  121. /package/docs/{cover.md → _cover.md} +0 -0
@@ -0,0 +1,436 @@
1
+ const libPictView = require('pict-view');
2
+ const libPictSectionFlow = require('pict-section-flow');
3
+
4
+ // Flow control cards
5
+ const libFlowCardStart = require('../cards/FlowCard-Start.js');
6
+ const libFlowCardEnd = require('../cards/FlowCard-End.js');
7
+
8
+ // Core/Control cards
9
+ const libFlowCardCommand = require('../cards/FlowCard-Command.js');
10
+ const libFlowCardConditional = require('../cards/FlowCard-Conditional.js');
11
+ const libFlowCardSolver = require('../cards/FlowCard-Solver.js');
12
+ const libFlowCardTemplateString = require('../cards/FlowCard-TemplateString.js');
13
+ const libFlowCardLaunchOperation = require('../cards/FlowCard-LaunchOperation.js');
14
+
15
+ // File I/O cards
16
+ const libFlowCardReadText = require('../cards/FlowCard-ReadText.js');
17
+ const libFlowCardWriteText = require('../cards/FlowCard-WriteText.js');
18
+ const libFlowCardReadJSON = require('../cards/FlowCard-ReadJSON.js');
19
+ const libFlowCardWriteJSON = require('../cards/FlowCard-WriteJSON.js');
20
+ const libFlowCardListFiles = require('../cards/FlowCard-ListFiles.js');
21
+ const libFlowCardCopyFile = require('../cards/FlowCard-CopyFile.js');
22
+
23
+ // REST/HTTP cards
24
+ const libFlowCardGetJSON = require('../cards/FlowCard-GetJSON.js');
25
+ const libFlowCardGetText = require('../cards/FlowCard-GetText.js');
26
+ const libFlowCardSendJSON = require('../cards/FlowCard-SendJSON.js');
27
+ const libFlowCardRestRequest = require('../cards/FlowCard-RestRequest.js');
28
+
29
+ // Meadow cards
30
+ const libFlowCardMeadowCreate = require('../cards/FlowCard-MeadowCreate.js');
31
+ const libFlowCardMeadowRead = require('../cards/FlowCard-MeadowRead.js');
32
+ const libFlowCardMeadowReads = require('../cards/FlowCard-MeadowReads.js');
33
+ const libFlowCardMeadowUpdate = require('../cards/FlowCard-MeadowUpdate.js');
34
+ const libFlowCardMeadowDelete = require('../cards/FlowCard-MeadowDelete.js');
35
+ const libFlowCardMeadowCount = require('../cards/FlowCard-MeadowCount.js');
36
+ const libFlowCardCSVTransform = require('../cards/FlowCard-CSVTransform.js');
37
+ const libFlowCardComprehensionIntersect = require('../cards/FlowCard-ComprehensionIntersect.js');
38
+
39
+ // Pipeline cards
40
+ const libFlowCardParseCSV = require('../cards/FlowCard-ParseCSV.js');
41
+ const libFlowCardHistogram = require('../cards/FlowCard-Histogram.js');
42
+
43
+ const _ViewConfiguration =
44
+ {
45
+ ViewIdentifier: "Ultravisor-FlowEditor",
46
+
47
+ DefaultRenderable: "Ultravisor-FlowEditor-Content",
48
+ DefaultDestinationAddress: "#Ultravisor-Content-Container",
49
+
50
+ AutoRender: false,
51
+
52
+ CSS: /*css*/`
53
+ .ultravisor-floweditor {
54
+ padding: 0.75em;
55
+ display: flex;
56
+ flex-direction: column;
57
+ flex: 1;
58
+ height: calc(100vh - 56px - 39px - 0.75em);
59
+ min-height: 400px;
60
+ box-sizing: border-box;
61
+ }
62
+ .ultravisor-floweditor-header {
63
+ flex-shrink: 0;
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: space-between;
67
+ margin-bottom: 0.75em;
68
+ padding-bottom: 0.75em;
69
+ border-bottom: 1px solid #2a2a4a;
70
+ }
71
+ .ultravisor-floweditor-header h1 {
72
+ margin: 0;
73
+ font-size: 1.8em;
74
+ font-weight: 300;
75
+ color: #e0e0e0;
76
+ }
77
+ .ultravisor-flow-actions {
78
+ display: flex;
79
+ gap: 0.5em;
80
+ align-items: center;
81
+ flex-wrap: wrap;
82
+ }
83
+ .ultravisor-flow-meta {
84
+ flex-shrink: 0;
85
+ display: flex;
86
+ gap: 0.75em;
87
+ align-items: center;
88
+ margin-bottom: 0.5em;
89
+ padding-bottom: 0.5em;
90
+ border-bottom: 1px solid #1a1a2e;
91
+ }
92
+ .ultravisor-flow-meta label {
93
+ font-size: 0.8em;
94
+ font-weight: 600;
95
+ color: #78909c;
96
+ text-transform: uppercase;
97
+ margin-right: 0.25em;
98
+ }
99
+ .ultravisor-flow-meta input {
100
+ flex: 1;
101
+ min-width: 120px;
102
+ }
103
+ .ultravisor-flow-meta-hash {
104
+ font-size: 0.8em;
105
+ color: #607d8b;
106
+ font-family: monospace;
107
+ }
108
+ #Ultravisor-FlowEditor-Container {
109
+ flex: 1;
110
+ min-height: 0;
111
+ }
112
+ `,
113
+
114
+ Templates:
115
+ [
116
+ {
117
+ Hash: "Ultravisor-FlowEditor-Template",
118
+ Template: /*html*/`
119
+ <div class="ultravisor-floweditor">
120
+ <div class="ultravisor-floweditor-header">
121
+ <h1 id="Ultravisor-FlowEditor-Title">Flow Editor</h1>
122
+ <div class="ultravisor-flow-actions">
123
+ <button class="ultravisor-btn ultravisor-btn-primary" onclick="{~P~}.views['Ultravisor-FlowEditor'].saveOperation()">Save Operation</button>
124
+ </div>
125
+ </div>
126
+ <div class="ultravisor-flow-meta">
127
+ <span id="Ultravisor-FlowEditor-HashDisplay" class="ultravisor-flow-meta-hash"></span>
128
+ <label>Name</label>
129
+ <input type="text" id="Ultravisor-FlowEditor-Name" placeholder="Operation name...">
130
+ <label>Description</label>
131
+ <input type="text" id="Ultravisor-FlowEditor-Description" placeholder="Description...">
132
+ </div>
133
+ <div id="Ultravisor-FlowEditor-Container"></div>
134
+ </div>
135
+ `
136
+ }
137
+ ],
138
+
139
+ Renderables:
140
+ [
141
+ {
142
+ RenderableHash: "Ultravisor-FlowEditor-Content",
143
+ TemplateHash: "Ultravisor-FlowEditor-Template",
144
+ DestinationAddress: "#Ultravisor-Content-Container",
145
+ RenderMethod: "replace"
146
+ }
147
+ ]
148
+ };
149
+
150
+ class UltravisorFlowEditorView extends libPictView
151
+ {
152
+ constructor(pFable, pOptions, pServiceHash)
153
+ {
154
+ super(pFable, pOptions, pServiceHash);
155
+
156
+ this._FlowView = null;
157
+ }
158
+
159
+ /**
160
+ * Build a map of FlowCard node type configurations keyed by hash.
161
+ * These are passed as NodeTypes in the FlowView options so they
162
+ * are available from the moment the NodeTypeProvider is created,
163
+ * before the toolbar renders.
164
+ */
165
+ _buildFlowCardNodeTypes()
166
+ {
167
+ let tmpCardClasses =
168
+ [
169
+ // Flow control
170
+ libFlowCardStart,
171
+ libFlowCardEnd,
172
+ // Core
173
+ libFlowCardCommand,
174
+ libFlowCardConditional,
175
+ libFlowCardSolver,
176
+ libFlowCardTemplateString,
177
+ libFlowCardLaunchOperation,
178
+ // File I/O
179
+ libFlowCardReadText,
180
+ libFlowCardWriteText,
181
+ libFlowCardReadJSON,
182
+ libFlowCardWriteJSON,
183
+ libFlowCardListFiles,
184
+ libFlowCardCopyFile,
185
+ // REST
186
+ libFlowCardGetJSON,
187
+ libFlowCardGetText,
188
+ libFlowCardSendJSON,
189
+ libFlowCardRestRequest,
190
+ // Meadow
191
+ libFlowCardMeadowCreate,
192
+ libFlowCardMeadowRead,
193
+ libFlowCardMeadowReads,
194
+ libFlowCardMeadowUpdate,
195
+ libFlowCardMeadowDelete,
196
+ libFlowCardMeadowCount,
197
+ libFlowCardCSVTransform,
198
+ libFlowCardComprehensionIntersect,
199
+ // Pipeline
200
+ libFlowCardParseCSV,
201
+ libFlowCardHistogram
202
+ ];
203
+
204
+ let tmpNodeTypes = {};
205
+
206
+ for (let i = 0; i < tmpCardClasses.length; i++)
207
+ {
208
+ let tmpCard = new tmpCardClasses[i](this.fable, {}, `FlowCard-${i}`);
209
+ let tmpConfig = tmpCard.getNodeTypeConfiguration();
210
+ // Ultravisor flow cards render port labels outside the node
211
+ // boundary to avoid overlapping body content
212
+ tmpConfig.PortLabelsOutside = true;
213
+ tmpNodeTypes[tmpConfig.Hash] = tmpConfig;
214
+ }
215
+
216
+ return tmpNodeTypes;
217
+ }
218
+
219
+ onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)
220
+ {
221
+ // Populate the metadata fields from CurrentEditOperation
222
+ let tmpOp = this.pict.AppData.Ultravisor.CurrentEditOperation;
223
+ if (tmpOp)
224
+ {
225
+ let tmpTitleEl = document.getElementById('Ultravisor-FlowEditor-Title');
226
+ if (tmpTitleEl)
227
+ {
228
+ tmpTitleEl.textContent = tmpOp.Hash ? ('Flow Editor: ' + (tmpOp.Name || tmpOp.Hash)) : 'Flow Editor: New Operation';
229
+ }
230
+
231
+ let tmpHashEl = document.getElementById('Ultravisor-FlowEditor-HashDisplay');
232
+ if (tmpHashEl && tmpOp.Hash)
233
+ {
234
+ tmpHashEl.textContent = tmpOp.Hash;
235
+ }
236
+
237
+ let tmpNameEl = document.getElementById('Ultravisor-FlowEditor-Name');
238
+ if (tmpNameEl)
239
+ {
240
+ tmpNameEl.value = tmpOp.Name || '';
241
+ }
242
+
243
+ let tmpDescEl = document.getElementById('Ultravisor-FlowEditor-Description');
244
+ if (tmpDescEl)
245
+ {
246
+ tmpDescEl.value = tmpOp.Description || '';
247
+ }
248
+ }
249
+
250
+ // Create and render the flow section view into its container
251
+ if (!this._FlowView)
252
+ {
253
+ this._FlowView = this.pict.addView('Ultravisor-FlowDiagram',
254
+ {
255
+ ViewIdentifier: 'Ultravisor-FlowDiagram',
256
+
257
+ DefaultRenderable: 'Flow-Container',
258
+ DefaultDestinationAddress: '#Ultravisor-FlowEditor-Container',
259
+
260
+ AutoRender: false,
261
+
262
+ FlowDataAddress: 'AppData.Ultravisor.Flows.Current',
263
+
264
+ TargetElementAddress: '#Flow-SVG-Container',
265
+
266
+ EnableToolbar: true,
267
+ EnablePanning: true,
268
+ EnableZooming: true,
269
+ EnableNodeDragging: true,
270
+ EnableConnectionCreation: true,
271
+ EnableGridSnap: false,
272
+ GridSnapSize: 20,
273
+
274
+ MinZoom: 0.1,
275
+ MaxZoom: 5.0,
276
+ ZoomStep: 0.1,
277
+
278
+ DefaultNodeType: 'default',
279
+ DefaultNodeWidth: 180,
280
+ DefaultNodeHeight: 80,
281
+
282
+ // Pre-register FlowCard node types so they are available
283
+ // when the NodeTypeProvider is created, before toolbar renders
284
+ NodeTypes: this._buildFlowCardNodeTypes(),
285
+
286
+ Renderables:
287
+ [
288
+ {
289
+ RenderableHash: 'Flow-Container',
290
+ TemplateHash: 'Flow-Container-Template',
291
+ DestinationAddress: '#Ultravisor-FlowEditor-Container',
292
+ RenderMethod: 'replace'
293
+ }
294
+ ]
295
+ },
296
+ libPictSectionFlow
297
+ );
298
+ }
299
+
300
+ // Initialize default empty flow if none exists
301
+ if (!this.pict.AppData.Ultravisor.Flows.Current)
302
+ {
303
+ this.pict.AppData.Ultravisor.Flows.Current =
304
+ {
305
+ Nodes: [],
306
+ Connections: [],
307
+ ViewState: { PanX: 0, PanY: 0, Zoom: 1, SelectedNodeHash: null, SelectedConnectionHash: null }
308
+ };
309
+ }
310
+
311
+ // Reset the flow view's render state so it re-initializes SVG elements
312
+ // when re-rendered (e.g. after navigating away and back)
313
+ this._FlowView.initialRenderComplete = false;
314
+ this._FlowView.render();
315
+
316
+ // Re-inject CSS after the flow view creates its dynamic styles
317
+ this.pict.CSSMap.injectCSS();
318
+
319
+ return super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);
320
+ }
321
+
322
+ loadExample(pExampleName)
323
+ {
324
+ let tmpFlowData = null;
325
+
326
+ if (pExampleName === 'CSVPipeline')
327
+ {
328
+ tmpFlowData = require('../data/ExampleFlow-CSVPipeline.js');
329
+ }
330
+ else if (pExampleName === 'MeadowPipeline')
331
+ {
332
+ tmpFlowData = require('../data/ExampleFlow-MeadowPipeline.js');
333
+ }
334
+ else if (pExampleName === 'FileProcessor')
335
+ {
336
+ tmpFlowData = require('../data/ExampleFlow-FileProcessor.js');
337
+ }
338
+
339
+ if (tmpFlowData)
340
+ {
341
+ this.pict.AppData.Ultravisor.Flows.Current = JSON.parse(JSON.stringify(tmpFlowData));
342
+ if (this._FlowView)
343
+ {
344
+ this._FlowView.setFlowData(this.pict.AppData.Ultravisor.Flows.Current);
345
+ // Zoom to fit after a brief delay to allow the DOM to update
346
+ let tmpFlowView = this._FlowView;
347
+ setTimeout(function()
348
+ {
349
+ if (tmpFlowView._ViewportManager)
350
+ {
351
+ tmpFlowView._ViewportManager.zoomToFit();
352
+ }
353
+ }, 100);
354
+ }
355
+ }
356
+ }
357
+
358
+ saveOperation()
359
+ {
360
+ if (!this._FlowView)
361
+ {
362
+ return;
363
+ }
364
+
365
+ let tmpFlowData = this._FlowView.getFlowData();
366
+ if (!tmpFlowData || !tmpFlowData.Nodes || tmpFlowData.Nodes.length === 0)
367
+ {
368
+ alert('No flow data to save. Add some nodes first.');
369
+ return;
370
+ }
371
+
372
+ // Read the metadata from the form fields
373
+ let tmpName = document.getElementById('Ultravisor-FlowEditor-Name').value.trim();
374
+ let tmpDescription = document.getElementById('Ultravisor-FlowEditor-Description').value.trim();
375
+
376
+ if (!tmpName)
377
+ {
378
+ alert('Please enter an operation name.');
379
+ return;
380
+ }
381
+
382
+ // Build the operation data with the graph
383
+ let tmpOpData =
384
+ {
385
+ Name: tmpName,
386
+ Description: tmpDescription,
387
+ Graph:
388
+ {
389
+ Nodes: tmpFlowData.Nodes,
390
+ Connections: tmpFlowData.Connections,
391
+ ViewState: tmpFlowData.ViewState || {}
392
+ }
393
+ };
394
+
395
+ // Include Hash if editing an existing operation
396
+ let tmpOp = this.pict.AppData.Ultravisor.CurrentEditOperation;
397
+ if (tmpOp && tmpOp.Hash)
398
+ {
399
+ tmpOpData.Hash = tmpOp.Hash;
400
+ }
401
+
402
+ this.pict.PictApplication.saveOperation(tmpOpData,
403
+ function (pError, pData)
404
+ {
405
+ if (pError)
406
+ {
407
+ alert('Error saving operation: ' + pError.message);
408
+ return;
409
+ }
410
+
411
+ // Update the current edit operation with the saved data
412
+ if (pData && pData.Hash)
413
+ {
414
+ if (!this.pict.AppData.Ultravisor.CurrentEditOperation)
415
+ {
416
+ this.pict.AppData.Ultravisor.CurrentEditOperation = {};
417
+ }
418
+ this.pict.AppData.Ultravisor.CurrentEditOperation.Hash = pData.Hash;
419
+ this.pict.AppData.Ultravisor.CurrentEditOperation.Name = tmpName;
420
+ this.pict.AppData.Ultravisor.CurrentEditOperation.Description = tmpDescription;
421
+
422
+ let tmpHashEl = document.getElementById('Ultravisor-FlowEditor-HashDisplay');
423
+ if (tmpHashEl)
424
+ {
425
+ tmpHashEl.textContent = pData.Hash;
426
+ }
427
+ }
428
+
429
+ alert('Operation saved successfully.');
430
+ }.bind(this));
431
+ }
432
+ }
433
+
434
+ module.exports = UltravisorFlowEditorView;
435
+
436
+ module.exports.default_configuration = _ViewConfiguration;
@@ -58,6 +58,10 @@ const _ViewConfiguration =
58
58
  background-color: #c62828;
59
59
  color: #ffcdd2;
60
60
  }
61
+ .ultravisor-manifest-status.waiting {
62
+ background-color: #f57f17;
63
+ color: #fff9c4;
64
+ }
61
65
  .ultravisor-manifest-detail {
62
66
  background: #16213e;
63
67
  border: 1px solid #2a2a4a;
@@ -101,12 +105,6 @@ const _ViewConfiguration =
101
105
  overflow-y: auto;
102
106
  margin-top: 0.5em;
103
107
  }
104
- .ultravisor-manifest-success-true {
105
- color: #66bb6a;
106
- }
107
- .ultravisor-manifest-success-false {
108
- color: #ef5350;
109
- }
110
108
  `,
111
109
 
112
110
  Templates:
@@ -169,28 +167,28 @@ class UltravisorManifestListView extends libPictView
169
167
  }
170
168
 
171
169
  let tmpHTML = '<table class="ultravisor-manifest-table">';
172
- tmpHTML += '<thead><tr><th>Run GUID</th><th>Operation</th><th>Status</th><th>Success</th><th>Started</th><th>Actions</th></tr></thead>';
170
+ tmpHTML += '<thead><tr><th>Run Hash</th><th>Operation</th><th>Status</th><th>Elapsed</th><th>Started</th><th>Actions</th></tr></thead>';
173
171
  tmpHTML += '<tbody>';
174
172
 
175
173
  for (let i = 0; i < tmpManifests.length; i++)
176
174
  {
177
175
  let tmpManifest = tmpManifests[i];
178
- let tmpGUIDRun = tmpManifest.GUIDRun || '';
179
- let tmpEscGUID = tmpGUIDRun.replace(/'/g, "\\'");
176
+ let tmpRunHash = tmpManifest.Hash || '';
177
+ let tmpEscHash = tmpRunHash.replace(/'/g, "\\'");
180
178
  let tmpStatus = tmpManifest.Status || 'Unknown';
181
179
  let tmpStatusClass = tmpStatus.toLowerCase();
182
- if (tmpStatusClass !== 'complete' && tmpStatusClass !== 'running' && tmpStatusClass !== 'error')
180
+ if (tmpStatusClass !== 'complete' && tmpStatusClass !== 'running' && tmpStatusClass !== 'error' && tmpStatusClass !== 'waiting')
183
181
  {
184
182
  tmpStatusClass = '';
185
183
  }
186
184
 
187
185
  tmpHTML += '<tr>';
188
- tmpHTML += '<td><code style="font-size:0.8em;">' + tmpGUIDRun + '</code></td>';
189
- tmpHTML += '<td>' + (tmpManifest.Name || tmpManifest.GUIDOperation || '') + '</td>';
190
- tmpHTML += '<td><span class="ultravisor-manifest-status ' + tmpStatusClass + '">' + tmpStatus + '</span></td>';
191
- tmpHTML += '<td><span class="ultravisor-manifest-success-' + (tmpManifest.Success ? 'true' : 'false') + '">' + (tmpManifest.Success ? 'Yes' : 'No') + '</span></td>';
192
- tmpHTML += '<td>' + (tmpManifest.StartTime || '') + '</td>';
193
- tmpHTML += '<td><button class="ultravisor-btn-sm ultravisor-btn-edit" onclick="' + tmpViewRef + '.showManifestDetail(\'' + tmpEscGUID + '\')">Details</button></td>';
186
+ tmpHTML += '<td><code style="font-size:0.8em;">' + this.escapeHTML(tmpRunHash) + '</code></td>';
187
+ tmpHTML += '<td>' + this.escapeHTML(tmpManifest.OperationHash || '') + '</td>';
188
+ tmpHTML += '<td><span class="ultravisor-manifest-status ' + tmpStatusClass + '">' + this.escapeHTML(tmpStatus) + '</span></td>';
189
+ tmpHTML += '<td>' + (tmpManifest.ElapsedMs ? tmpManifest.ElapsedMs + 'ms' : '') + '</td>';
190
+ tmpHTML += '<td>' + this.escapeHTML(tmpManifest.StartTime || '') + '</td>';
191
+ tmpHTML += '<td><button class="ultravisor-btn-sm ultravisor-btn-edit" onclick="' + tmpViewRef + '.showManifestDetail(\'' + tmpEscHash + '\')">Details</button></td>';
194
192
  tmpHTML += '</tr>';
195
193
  }
196
194
 
@@ -198,9 +196,9 @@ class UltravisorManifestListView extends libPictView
198
196
  this.pict.ContentAssignment.assignContent('#Ultravisor-ManifestList-Body', tmpHTML);
199
197
  }
200
198
 
201
- showManifestDetail(pGUIDRun)
199
+ showManifestDetail(pRunHash)
202
200
  {
203
- this.pict.PictApplication.loadManifest(pGUIDRun,
201
+ this.pict.PictApplication.loadManifest(pRunHash,
204
202
  function (pError, pManifest)
205
203
  {
206
204
  if (pError || !pManifest)
@@ -211,48 +209,52 @@ class UltravisorManifestListView extends libPictView
211
209
  }
212
210
 
213
211
  let tmpHTML = '<div class="ultravisor-manifest-detail visible">';
214
- tmpHTML += '<h3>Run: ' + (pManifest.GUIDRun || '') + '</h3>';
215
- tmpHTML += '<p><strong>Operation:</strong> ' + (pManifest.Name || pManifest.GUIDOperation || '') + '</p>';
216
- tmpHTML += '<p><strong>Status:</strong> ' + (pManifest.Status || '') + ' &middot; <strong>Success:</strong> ' + (pManifest.Success ? 'Yes' : 'No') + '</p>';
217
- tmpHTML += '<p><strong>Start:</strong> ' + (pManifest.StartTime || '') + ' &middot; <strong>Stop:</strong> ' + (pManifest.StopTime || '') + '</p>';
212
+ tmpHTML += '<h3>Run: ' + this.escapeHTML(pManifest.Hash || '') + '</h3>';
213
+ tmpHTML += '<p><strong>Operation:</strong> ' + this.escapeHTML(pManifest.OperationHash || '') + '</p>';
214
+ tmpHTML += '<p><strong>Status:</strong> ' + this.escapeHTML(pManifest.Status || '') + '</p>';
215
+ tmpHTML += '<p><strong>Start:</strong> ' + this.escapeHTML(pManifest.StartTime || '') + ' &middot; <strong>Stop:</strong> ' + this.escapeHTML(pManifest.StopTime || '') + '</p>';
216
+ tmpHTML += '<p><strong>Elapsed:</strong> ' + (pManifest.ElapsedMs || 0) + 'ms</p>';
218
217
 
219
- if (pManifest.Summary)
218
+ // Task Outputs
219
+ if (pManifest.TaskOutputs && Object.keys(pManifest.TaskOutputs).length > 0)
220
220
  {
221
- tmpHTML += '<p><strong>Summary:</strong> ' + this.escapeHTML(pManifest.Summary) + '</p>';
221
+ tmpHTML += '<h3>Task Outputs</h3>';
222
+ tmpHTML += '<div class="ultravisor-manifest-output">' + this.escapeHTML(JSON.stringify(pManifest.TaskOutputs, null, 2)) + '</div>';
222
223
  }
223
224
 
224
- // Task results
225
- let tmpTaskResults = pManifest.TaskResults || [];
226
- if (tmpTaskResults.length > 0)
225
+ // Task Manifests (object keyed by node hash)
226
+ if (pManifest.TaskManifests && Object.keys(pManifest.TaskManifests).length > 0)
227
227
  {
228
- tmpHTML += '<h3>Task Results</h3>';
229
- for (let i = 0; i < tmpTaskResults.length; i++)
228
+ tmpHTML += '<h3>Task Manifests</h3>';
229
+ let tmpNodeHashes = Object.keys(pManifest.TaskManifests);
230
+ for (let i = 0; i < tmpNodeHashes.length; i++)
230
231
  {
231
- let tmpResult = tmpTaskResults[i];
232
+ let tmpNodeHash = tmpNodeHashes[i];
233
+ let tmpTaskManifest = pManifest.TaskManifests[tmpNodeHash];
232
234
  tmpHTML += '<div class="ultravisor-manifest-task-result">';
233
235
  tmpHTML += '<div class="ultravisor-manifest-task-result-header">';
234
- tmpHTML += '<code>' + (tmpResult.GUIDTask || '') + '</code>';
235
- tmpHTML += '<span class="ultravisor-manifest-status ' + (tmpResult.Status || '').toLowerCase() + '">' + (tmpResult.Status || '') + '</span>';
236
+ tmpHTML += '<code>' + this.escapeHTML(tmpNodeHash) + '</code>';
237
+ tmpHTML += '<span class="ultravisor-manifest-status ' + (tmpTaskManifest.Status || '').toLowerCase() + '">' + this.escapeHTML(tmpTaskManifest.Status || '') + '</span>';
236
238
  tmpHTML += '</div>';
237
- tmpHTML += '<p style="margin:0.25em 0; font-size:0.85em; color:#78909c;">' + (tmpResult.Name || '') + ' (' + (tmpResult.Type || '') + ')</p>';
238
-
239
- if (tmpResult.Output)
240
- {
241
- tmpHTML += '<div class="ultravisor-manifest-output">' + this.escapeHTML(String(tmpResult.Output)) + '</div>';
242
- }
243
-
244
- if (tmpResult.Log && tmpResult.Log.length > 0)
239
+ if (tmpTaskManifest.Output)
245
240
  {
246
- tmpHTML += '<div class="ultravisor-manifest-output">' + this.escapeHTML(tmpResult.Log.join('\n')) + '</div>';
241
+ tmpHTML += '<div class="ultravisor-manifest-output">' + this.escapeHTML(JSON.stringify(tmpTaskManifest.Output, null, 2)) + '</div>';
247
242
  }
248
243
  tmpHTML += '</div>';
249
244
  }
250
245
  }
251
246
 
252
- // Operation log
247
+ // Errors
248
+ if (pManifest.Errors && pManifest.Errors.length > 0)
249
+ {
250
+ tmpHTML += '<h3 style="color:#ef5350;">Errors</h3>';
251
+ tmpHTML += '<div class="ultravisor-manifest-output" style="border: 1px solid #ef5350;">' + this.escapeHTML(pManifest.Errors.join('\n')) + '</div>';
252
+ }
253
+
254
+ // Log
253
255
  if (pManifest.Log && pManifest.Log.length > 0)
254
256
  {
255
- tmpHTML += '<h3>Operation Log</h3>';
257
+ tmpHTML += '<h3>Log</h3>';
256
258
  tmpHTML += '<div class="ultravisor-manifest-output">' + this.escapeHTML(pManifest.Log.join('\n')) + '</div>';
257
259
  }
258
260