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,231 @@
1
+ /**
2
+ * Example Flow: Config File Processor
3
+ *
4
+ * Demonstrates a file-based config processing pipeline using the new engine task types:
5
+ * Start -> Read File (config template) -> If Conditional (has placeholder?)
6
+ * -> True: Replace String (fill in value) -> Write File (output config) -> End
7
+ * -> False: Error Message (missing placeholder) -> End
8
+ *
9
+ * Shows branching, error handling, and file I/O with the event-driven execution engine.
10
+ */
11
+ module.exports =
12
+ {
13
+ Nodes:
14
+ [
15
+ // ── Entry ────────────────────────────────────────────────
16
+ {
17
+ Hash: 'cfg-start',
18
+ Type: 'start',
19
+ X: 50,
20
+ Y: 200,
21
+ Width: 140,
22
+ Height: 80,
23
+ Title: 'Start',
24
+ Ports:
25
+ [
26
+ { Hash: 'cfg-start-out', Direction: 'output', Side: 'right', Label: 'Out' }
27
+ ],
28
+ Data: {}
29
+ },
30
+ // ── Read the config template ─────────────────────────────
31
+ {
32
+ Hash: 'cfg-read',
33
+ Type: 'read-file',
34
+ X: 270,
35
+ Y: 180,
36
+ Width: 200,
37
+ Height: 80,
38
+ Title: 'Read Config Template',
39
+ Ports:
40
+ [
41
+ { Hash: 'cfg-read-in', Direction: 'input', Side: 'left', Label: 'BeginRead' },
42
+ { Hash: 'cfg-read-done', Direction: 'output', Side: 'right', Label: 'ReadComplete' },
43
+ { Hash: 'cfg-read-err', Direction: 'output', Side: 'bottom', Label: 'Error' }
44
+ ],
45
+ Data: { FilePath: 'config.template.json', Encoding: 'utf8' }
46
+ },
47
+ // ── Check if placeholder exists in the content ───────────
48
+ {
49
+ Hash: 'cfg-check',
50
+ Type: 'if-conditional',
51
+ X: 550,
52
+ Y: 160,
53
+ Width: 220,
54
+ Height: 100,
55
+ Title: 'Has Placeholder?',
56
+ Ports:
57
+ [
58
+ { Hash: 'cfg-check-in', Direction: 'input', Side: 'left', Label: 'Evaluate' },
59
+ { Hash: 'cfg-check-true', Direction: 'output', Side: 'right', Label: 'True' },
60
+ { Hash: 'cfg-check-false', Direction: 'output', Side: 'bottom', Label: 'False' }
61
+ ],
62
+ Data: { DataAddress: 'TaskOutput.cfg-read.FileContent', CompareValue: '{{API_KEY}}', Operator: 'contains' }
63
+ },
64
+ // ── Replace the placeholder with the real value ──────────
65
+ {
66
+ Hash: 'cfg-replace',
67
+ Type: 'replace-string',
68
+ X: 850,
69
+ Y: 140,
70
+ Width: 220,
71
+ Height: 80,
72
+ Title: 'Fill In API Key',
73
+ Ports:
74
+ [
75
+ { Hash: 'cfg-replace-in', Direction: 'input', Side: 'left', Label: 'Replace' },
76
+ { Hash: 'cfg-replace-done', Direction: 'output', Side: 'right', Label: 'ReplaceComplete' },
77
+ { Hash: 'cfg-replace-err', Direction: 'output', Side: 'bottom', Label: 'Error' }
78
+ ],
79
+ Data: { InputString: '{~D:Record.TaskOutput.cfg-read.FileContent~}', SearchString: '{{API_KEY}}', ReplaceString: 'sk-live-abc123def456' }
80
+ },
81
+ // ── Write the processed config ──────────────────────────
82
+ {
83
+ Hash: 'cfg-write',
84
+ Type: 'write-file',
85
+ X: 1150,
86
+ Y: 140,
87
+ Width: 200,
88
+ Height: 80,
89
+ Title: 'Write Config',
90
+ Ports:
91
+ [
92
+ { Hash: 'cfg-write-in', Direction: 'input', Side: 'left', Label: 'BeginWrite' },
93
+ { Hash: 'cfg-write-done', Direction: 'output', Side: 'right', Label: 'WriteComplete' },
94
+ { Hash: 'cfg-write-err', Direction: 'output', Side: 'bottom', Label: 'Error' }
95
+ ],
96
+ Data: { FilePath: 'config.json', Content: '{~D:Record.TaskOutput.cfg-replace.ReplacedString~}', Encoding: 'utf8' }
97
+ },
98
+ // ── Error: placeholder not found ─────────────────────────
99
+ {
100
+ Hash: 'cfg-error',
101
+ Type: 'error-message',
102
+ X: 850,
103
+ Y: 340,
104
+ Width: 220,
105
+ Height: 80,
106
+ Title: 'Missing Placeholder',
107
+ Ports:
108
+ [
109
+ { Hash: 'cfg-error-in', Direction: 'input', Side: 'left', Label: 'Trigger' },
110
+ { Hash: 'cfg-error-done', Direction: 'output', Side: 'right', Label: 'Complete' }
111
+ ],
112
+ Data: { MessageTemplate: 'Config template does not contain the {{API_KEY}} placeholder' }
113
+ },
114
+ // ── Exit ─────────────────────────────────────────────────
115
+ {
116
+ Hash: 'cfg-end',
117
+ Type: 'end',
118
+ X: 1430,
119
+ Y: 220,
120
+ Width: 140,
121
+ Height: 80,
122
+ Title: 'End',
123
+ Ports:
124
+ [
125
+ { Hash: 'cfg-end-in', Direction: 'input', Side: 'left', Label: 'In' }
126
+ ],
127
+ Data: {}
128
+ }
129
+ ],
130
+ Connections:
131
+ [
132
+ // Start -> Read Config Template
133
+ {
134
+ Hash: 'cfg-c1',
135
+ SourceNodeHash: 'cfg-start',
136
+ SourcePortHash: 'cfg-start-out',
137
+ TargetNodeHash: 'cfg-read',
138
+ TargetPortHash: 'cfg-read-in',
139
+ Data: {}
140
+ },
141
+ // Read Config Template -> Has Placeholder?
142
+ {
143
+ Hash: 'cfg-c2',
144
+ SourceNodeHash: 'cfg-read',
145
+ SourcePortHash: 'cfg-read-done',
146
+ TargetNodeHash: 'cfg-check',
147
+ TargetPortHash: 'cfg-check-in',
148
+ Data: {}
149
+ },
150
+ // Has Placeholder? (True) -> Fill In API Key
151
+ {
152
+ Hash: 'cfg-c3',
153
+ SourceNodeHash: 'cfg-check',
154
+ SourcePortHash: 'cfg-check-true',
155
+ TargetNodeHash: 'cfg-replace',
156
+ TargetPortHash: 'cfg-replace-in',
157
+ Data: {}
158
+ },
159
+ // Fill In API Key -> Write Config
160
+ {
161
+ Hash: 'cfg-c4',
162
+ SourceNodeHash: 'cfg-replace',
163
+ SourcePortHash: 'cfg-replace-done',
164
+ TargetNodeHash: 'cfg-write',
165
+ TargetPortHash: 'cfg-write-in',
166
+ Data: {}
167
+ },
168
+ // Write Config -> End
169
+ {
170
+ Hash: 'cfg-c5',
171
+ SourceNodeHash: 'cfg-write',
172
+ SourcePortHash: 'cfg-write-done',
173
+ TargetNodeHash: 'cfg-end',
174
+ TargetPortHash: 'cfg-end-in',
175
+ Data: {}
176
+ },
177
+ // Has Placeholder? (False) -> Missing Placeholder
178
+ {
179
+ Hash: 'cfg-c6',
180
+ SourceNodeHash: 'cfg-check',
181
+ SourcePortHash: 'cfg-check-false',
182
+ TargetNodeHash: 'cfg-error',
183
+ TargetPortHash: 'cfg-error-in',
184
+ Data: {}
185
+ },
186
+ // Missing Placeholder -> End
187
+ {
188
+ Hash: 'cfg-c7',
189
+ SourceNodeHash: 'cfg-error',
190
+ SourcePortHash: 'cfg-error-done',
191
+ TargetNodeHash: 'cfg-end',
192
+ TargetPortHash: 'cfg-end-in',
193
+ Data: {}
194
+ },
195
+ // Read Config Template error -> End
196
+ {
197
+ Hash: 'cfg-c8',
198
+ SourceNodeHash: 'cfg-read',
199
+ SourcePortHash: 'cfg-read-err',
200
+ TargetNodeHash: 'cfg-end',
201
+ TargetPortHash: 'cfg-end-in',
202
+ Data: {}
203
+ },
204
+ // Fill In API Key error -> End
205
+ {
206
+ Hash: 'cfg-c9',
207
+ SourceNodeHash: 'cfg-replace',
208
+ SourcePortHash: 'cfg-replace-err',
209
+ TargetNodeHash: 'cfg-end',
210
+ TargetPortHash: 'cfg-end-in',
211
+ Data: {}
212
+ },
213
+ // Write Config error -> End
214
+ {
215
+ Hash: 'cfg-c10',
216
+ SourceNodeHash: 'cfg-write',
217
+ SourcePortHash: 'cfg-write-err',
218
+ TargetNodeHash: 'cfg-end',
219
+ TargetPortHash: 'cfg-end-in',
220
+ Data: {}
221
+ }
222
+ ],
223
+ ViewState:
224
+ {
225
+ PanX: 0,
226
+ PanY: 0,
227
+ Zoom: 1,
228
+ SelectedNodeHash: null,
229
+ SelectedConnectionHash: null
230
+ }
231
+ };
@@ -0,0 +1,315 @@
1
+ /**
2
+ * Example Flow: File Processor (Search & Replace)
3
+ *
4
+ * Demonstrates a complete file processing pipeline with user input, looping,
5
+ * and error handling:
6
+ *
7
+ * Start
8
+ * -> Value Input (ask user for file path, stores to Operation.InputFilePath)
9
+ * -> Read File (loads the file at the user-provided path)
10
+ * -> [Error] -> Error Message -> End
11
+ * -> Split Execute (split file into lines)
12
+ * -> [per line] Replace String ("John" -> "Jane")
13
+ * -> String Appender (accumulate into Operation.OutputFileContents)
14
+ * -> [loop back: Append Completed -> Split StepComplete]
15
+ * -> [all lines done] Write File (save to original path + ".ultracopy")
16
+ * -> End
17
+ *
18
+ * This example showcases:
19
+ * - Value input pausing execution for user interaction
20
+ * - State connections (data flow between task settings/outputs)
21
+ * - The split-execute looping pattern with StepComplete feedback
22
+ * - Error branching
23
+ * - Pict template expressions for dynamic file paths
24
+ */
25
+ module.exports =
26
+ {
27
+ Nodes:
28
+ [
29
+ // ── Start ────────────────────────────────────────────────
30
+ {
31
+ Hash: 'fp-start',
32
+ Type: 'start',
33
+ X: 50,
34
+ Y: 200,
35
+ Width: 140,
36
+ Height: 80,
37
+ Title: 'Start',
38
+ Ports:
39
+ [
40
+ { Hash: 'fp-start-out', Direction: 'output', Side: 'right', Label: 'Out' }
41
+ ],
42
+ Data: {}
43
+ },
44
+ // ── Ask user for file path ───────────────────────────────
45
+ {
46
+ Hash: 'fp-input',
47
+ Type: 'value-input',
48
+ X: 280,
49
+ Y: 180,
50
+ Width: 220,
51
+ Height: 100,
52
+ Title: 'Enter File Path',
53
+ Ports:
54
+ [
55
+ { Hash: 'fp-input-req', Direction: 'input', Side: 'left-bottom', Label: 'RequestInput' },
56
+ { Hash: 'fp-input-done', Direction: 'output', Side: 'right', Label: 'ValueInputComplete' },
57
+ { Hash: 'fp-input-filepath', Direction: 'output', Side: 'right-top', Label: 'InputFilePath' }
58
+ ],
59
+ Data: { PromptMessage: 'Enter a file path and name', OutputAddress: 'Operation.InputFilePath' }
60
+ },
61
+ // ── Load the file ────────────────────────────────────────
62
+ {
63
+ Hash: 'fp-read',
64
+ Type: 'read-file',
65
+ X: 590,
66
+ Y: 180,
67
+ Width: 200,
68
+ Height: 100,
69
+ Title: 'Load File',
70
+ Ports:
71
+ [
72
+ { Hash: 'fp-read-begin', Direction: 'input', Side: 'left-bottom', Label: 'BeginRead' },
73
+ { Hash: 'fp-read-filepath', Direction: 'input', Side: 'left-top', Label: 'FilePath' },
74
+ { Hash: 'fp-read-done', Direction: 'output', Side: 'right', Label: 'ReadComplete' },
75
+ { Hash: 'fp-read-content', Direction: 'output', Side: 'right-top', Label: 'FileContent' },
76
+ { Hash: 'fp-read-err', Direction: 'output', Side: 'bottom', Label: 'Error' }
77
+ ],
78
+ Data: { FilePath: '{~D:Record.Operation.InputFilePath~}', Encoding: 'utf8' }
79
+ },
80
+ // ── Error handler for file read ──────────────────────────
81
+ {
82
+ Hash: 'fp-error',
83
+ Type: 'error-message',
84
+ X: 590,
85
+ Y: 420,
86
+ Width: 220,
87
+ Height: 80,
88
+ Title: 'Read Error',
89
+ Ports:
90
+ [
91
+ { Hash: 'fp-error-in', Direction: 'input', Side: 'left-bottom', Label: 'Trigger' },
92
+ { Hash: 'fp-error-done', Direction: 'output', Side: 'right', Label: 'Complete' }
93
+ ],
94
+ Data: { MessageTemplate: 'Failed to read file: {~D:Record.Operation.InputFilePath~}' }
95
+ },
96
+ // ── Split file into lines ────────────────────────────────
97
+ {
98
+ Hash: 'fp-split',
99
+ Type: 'split-execute',
100
+ X: 890,
101
+ Y: 160,
102
+ Width: 240,
103
+ Height: 120,
104
+ Title: 'Split Lines',
105
+ Ports:
106
+ [
107
+ { Hash: 'fp-split-begin', Direction: 'input', Side: 'left-bottom', Label: 'PerformSplit' },
108
+ { Hash: 'fp-split-step', Direction: 'input', Side: 'left-bottom', Label: 'StepComplete' },
109
+ { Hash: 'fp-split-inputstr', Direction: 'input', Side: 'left-top', Label: 'InputString' },
110
+ { Hash: 'fp-split-token', Direction: 'output', Side: 'right', Label: 'TokenDataSent' },
111
+ { Hash: 'fp-split-alldone', Direction: 'output', Side: 'right-bottom', Label: 'CompletedAllSubtasks' }
112
+ ],
113
+ Data: { InputString: '{~D:Record.TaskOutput.fp-read.FileContent~}', SplitDelimiter: '\n' }
114
+ },
115
+ // ── Replace "John" with "Jane" in each line ──────────────
116
+ {
117
+ Hash: 'fp-replace',
118
+ Type: 'replace-string',
119
+ X: 1230,
120
+ Y: 160,
121
+ Width: 220,
122
+ Height: 100,
123
+ Title: 'Replace John with Jane',
124
+ Ports:
125
+ [
126
+ { Hash: 'fp-replace-in', Direction: 'input', Side: 'left-bottom', Label: 'Replace' },
127
+ { Hash: 'fp-replace-done', Direction: 'output', Side: 'right', Label: 'ReplaceComplete' },
128
+ { Hash: 'fp-replace-result', Direction: 'output', Side: 'right-top', Label: 'ReplacedString' }
129
+ ],
130
+ Data: { InputString: '{~D:Record.TaskOutput.fp-split.CurrentToken~}', SearchString: 'John', ReplaceString: 'Jane' }
131
+ },
132
+ // ── Append each processed line to output ─────────────────
133
+ {
134
+ Hash: 'fp-append',
135
+ Type: 'string-appender',
136
+ X: 1540,
137
+ Y: 160,
138
+ Width: 220,
139
+ Height: 100,
140
+ Title: 'Append Line',
141
+ Ports:
142
+ [
143
+ { Hash: 'fp-append-in', Direction: 'input', Side: 'left-bottom', Label: 'Append' },
144
+ { Hash: 'fp-append-inputstr', Direction: 'input', Side: 'left-top', Label: 'InputString' },
145
+ { Hash: 'fp-append-done', Direction: 'output', Side: 'right', Label: 'Completed' }
146
+ ],
147
+ Data: { InputString: '{~D:Record.TaskOutput.fp-replace.ReplacedString~}', OutputAddress: 'Operation.OutputFileContents', AppendNewline: true }
148
+ },
149
+ // ── Write the processed file ─────────────────────────────
150
+ {
151
+ Hash: 'fp-write',
152
+ Type: 'write-file',
153
+ X: 1230,
154
+ Y: 420,
155
+ Width: 220,
156
+ Height: 80,
157
+ Title: 'Save File',
158
+ Ports:
159
+ [
160
+ { Hash: 'fp-write-begin', Direction: 'input', Side: 'left-bottom', Label: 'BeginWrite' },
161
+ { Hash: 'fp-write-done', Direction: 'output', Side: 'right', Label: 'WriteComplete' },
162
+ { Hash: 'fp-write-err', Direction: 'output', Side: 'bottom', Label: 'Error' }
163
+ ],
164
+ Data: { FilePath: '{~D:Record.Operation.InputFilePath~}.ultracopy', Content: '{~D:Record.Operation.OutputFileContents~}', Encoding: 'utf8' }
165
+ },
166
+ // ── End ──────────────────────────────────────────────────
167
+ {
168
+ Hash: 'fp-end',
169
+ Type: 'end',
170
+ X: 1540,
171
+ Y: 420,
172
+ Width: 140,
173
+ Height: 80,
174
+ Title: 'End',
175
+ Ports:
176
+ [
177
+ { Hash: 'fp-end-in', Direction: 'input', Side: 'left-bottom', Label: 'In' }
178
+ ],
179
+ Data: {}
180
+ }
181
+ ],
182
+ Connections:
183
+ [
184
+ // ── Event connections (execution flow) ───────────────────
185
+
186
+ // Start -> Enter File Path (step 22)
187
+ {
188
+ Hash: 'fp-ev1',
189
+ SourceNodeHash: 'fp-start',
190
+ SourcePortHash: 'fp-start-out',
191
+ TargetNodeHash: 'fp-input',
192
+ TargetPortHash: 'fp-input-req',
193
+ Data: {}
194
+ },
195
+ // Enter File Path -> Load File (step 23)
196
+ {
197
+ Hash: 'fp-ev2',
198
+ SourceNodeHash: 'fp-input',
199
+ SourcePortHash: 'fp-input-done',
200
+ TargetNodeHash: 'fp-read',
201
+ TargetPortHash: 'fp-read-begin',
202
+ Data: {}
203
+ },
204
+ // Load File ReadComplete -> Split Lines PerformSplit (step 24)
205
+ {
206
+ Hash: 'fp-ev3',
207
+ SourceNodeHash: 'fp-read',
208
+ SourcePortHash: 'fp-read-done',
209
+ TargetNodeHash: 'fp-split',
210
+ TargetPortHash: 'fp-split-begin',
211
+ Data: {}
212
+ },
213
+ // Load File Error -> Read Error (step 10)
214
+ {
215
+ Hash: 'fp-ev4',
216
+ SourceNodeHash: 'fp-read',
217
+ SourcePortHash: 'fp-read-err',
218
+ TargetNodeHash: 'fp-error',
219
+ TargetPortHash: 'fp-error-in',
220
+ Data: {}
221
+ },
222
+ // Read Error -> End
223
+ {
224
+ Hash: 'fp-ev5',
225
+ SourceNodeHash: 'fp-error',
226
+ SourcePortHash: 'fp-error-done',
227
+ TargetNodeHash: 'fp-end',
228
+ TargetPortHash: 'fp-end-in',
229
+ Data: {}
230
+ },
231
+ // Split Lines TokenDataSent -> Replace (step 25)
232
+ {
233
+ Hash: 'fp-ev6',
234
+ SourceNodeHash: 'fp-split',
235
+ SourcePortHash: 'fp-split-token',
236
+ TargetNodeHash: 'fp-replace',
237
+ TargetPortHash: 'fp-replace-in',
238
+ Data: {}
239
+ },
240
+ // Replace ReplaceComplete -> Append (step 26)
241
+ {
242
+ Hash: 'fp-ev7',
243
+ SourceNodeHash: 'fp-replace',
244
+ SourcePortHash: 'fp-replace-done',
245
+ TargetNodeHash: 'fp-append',
246
+ TargetPortHash: 'fp-append-in',
247
+ Data: {}
248
+ },
249
+ // Append Completed -> Split StepComplete (loop back for next token)
250
+ {
251
+ Hash: 'fp-ev8',
252
+ SourceNodeHash: 'fp-append',
253
+ SourcePortHash: 'fp-append-done',
254
+ TargetNodeHash: 'fp-split',
255
+ TargetPortHash: 'fp-split-step',
256
+ Data: {}
257
+ },
258
+ // Split Lines CompletedAllSubtasks -> Save File
259
+ {
260
+ Hash: 'fp-ev9',
261
+ SourceNodeHash: 'fp-split',
262
+ SourcePortHash: 'fp-split-alldone',
263
+ TargetNodeHash: 'fp-write',
264
+ TargetPortHash: 'fp-write-begin',
265
+ Data: {}
266
+ },
267
+ // Save File WriteComplete -> End (step 21)
268
+ {
269
+ Hash: 'fp-ev10',
270
+ SourceNodeHash: 'fp-write',
271
+ SourcePortHash: 'fp-write-done',
272
+ TargetNodeHash: 'fp-end',
273
+ TargetPortHash: 'fp-end-in',
274
+ Data: {}
275
+ },
276
+
277
+ // ── State connections (data flow) ────────────────────────
278
+
279
+ // Enter File Path OutputAddress -> Load File FilePath (step 8)
280
+ {
281
+ Hash: 'fp-st1',
282
+ SourceNodeHash: 'fp-input',
283
+ SourcePortHash: 'fp-input-filepath',
284
+ TargetNodeHash: 'fp-read',
285
+ TargetPortHash: 'fp-read-filepath',
286
+ Data: {}
287
+ },
288
+ // Load File FileContent -> Split Lines InputString (step 13)
289
+ {
290
+ Hash: 'fp-st2',
291
+ SourceNodeHash: 'fp-read',
292
+ SourcePortHash: 'fp-read-content',
293
+ TargetNodeHash: 'fp-split',
294
+ TargetPortHash: 'fp-split-inputstr',
295
+ Data: {}
296
+ },
297
+ // Replace ReplacedString -> Append InputString (step 15)
298
+ {
299
+ Hash: 'fp-st3',
300
+ SourceNodeHash: 'fp-replace',
301
+ SourcePortHash: 'fp-replace-result',
302
+ TargetNodeHash: 'fp-append',
303
+ TargetPortHash: 'fp-append-inputstr',
304
+ Data: {}
305
+ }
306
+ ],
307
+ ViewState:
308
+ {
309
+ PanX: 0,
310
+ PanY: 0,
311
+ Zoom: 1,
312
+ SelectedNodeHash: null,
313
+ SelectedConnectionHash: null
314
+ }
315
+ };