@node-red/runtime 3.1.0-beta.2 → 3.1.0-beta.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/lib/flows/Flow.js +140 -164
- package/lib/flows/Group.js +55 -0
- package/lib/flows/Subflow.js +19 -52
- package/lib/flows/index.js +14 -20
- package/lib/flows/util.js +478 -414
- package/lib/index.js +9 -0
- package/lib/nodes/index.js +0 -1
- package/package.json +4 -4
package/lib/flows/util.js
CHANGED
|
@@ -13,23 +13,32 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
**/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const credentials = require("../nodes/credentials");
|
|
16
|
+
const clone = require("clone");
|
|
17
|
+
const redUtil = require("@node-red/util").util;
|
|
18
|
+
const Log = require("@node-red/util").log;
|
|
19
|
+
const typeRegistry = require("@node-red/registry");
|
|
20
|
+
const subflowInstanceRE = /^subflow:(.+)$/;
|
|
22
21
|
|
|
23
22
|
let _runtime = null;
|
|
23
|
+
let envVarExcludes = {};
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
function init(runtime) {
|
|
26
|
+
_runtime = runtime;
|
|
27
|
+
envVarExcludes = {};
|
|
28
|
+
if (runtime.settings.hasOwnProperty('envVarExcludes') && Array.isArray(runtime.settings.envVarExcludes)) {
|
|
29
|
+
runtime.settings.envVarExcludes.forEach(v => envVarExcludes[v] = true);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
26
32
|
|
|
27
33
|
function diffNodes(oldNode,newNode) {
|
|
28
34
|
if (oldNode == null) {
|
|
29
35
|
return true;
|
|
30
36
|
}
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
const keyFilter = p => p != 'x' && p != 'y' && p != 'wires'
|
|
38
|
+
const groupKeyFilter = p => keyFilter(p) && p != 'nodes' && p != 'style' && p != 'w' && p != 'h'
|
|
39
|
+
var oldKeys = Object.keys(oldNode).filter(oldNode.type === 'group' ? groupKeyFilter : keyFilter);
|
|
40
|
+
var newKeys = Object.keys(newNode).filter(newNode.type === 'group' ? groupKeyFilter : keyFilter);
|
|
41
|
+
|
|
33
42
|
if (oldKeys.length != newKeys.length) {
|
|
34
43
|
return true;
|
|
35
44
|
}
|
|
@@ -70,8 +79,64 @@ function mapEnvVarProperties(obj,prop,flow,config) {
|
|
|
70
79
|
}
|
|
71
80
|
}
|
|
72
81
|
|
|
82
|
+
async function evaluateEnvProperties(flow, env, credentials) {
|
|
83
|
+
const pendingEvaluations = []
|
|
84
|
+
const evaluatedEnv = {}
|
|
85
|
+
const envTypes = []
|
|
86
|
+
for (let i = 0; i < env.length; i++) {
|
|
87
|
+
let { name, value, type } = env[i]
|
|
88
|
+
if (type === "env") {
|
|
89
|
+
// Do env types last as they may include references to other env vars
|
|
90
|
+
// at this level which need to be resolved before they can be looked-up
|
|
91
|
+
envTypes.push(env[i])
|
|
92
|
+
} else if (type === "bool") {
|
|
93
|
+
value = (value === "true") || (value === true);
|
|
94
|
+
} else if (type === "cred") {
|
|
95
|
+
if (credentials.hasOwnProperty(name)) {
|
|
96
|
+
value = credentials[name];
|
|
97
|
+
}
|
|
98
|
+
} else if (type ==='jsonata') {
|
|
99
|
+
pendingEvaluations.push(new Promise((resolve, _) => {
|
|
100
|
+
redUtil.evaluateNodeProperty(value, 'jsonata', {_flow: flow}, null, (err, result) => {
|
|
101
|
+
if (!err) {
|
|
102
|
+
evaluatedEnv[name] = result
|
|
103
|
+
}
|
|
104
|
+
resolve()
|
|
105
|
+
});
|
|
106
|
+
}))
|
|
107
|
+
} else {
|
|
108
|
+
value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null);
|
|
109
|
+
}
|
|
110
|
+
evaluatedEnv[name] = value
|
|
111
|
+
}
|
|
112
|
+
if (pendingEvaluations.length > 0) {
|
|
113
|
+
await Promise.all(pendingEvaluations)
|
|
114
|
+
}
|
|
115
|
+
for (let i = 0; i < envTypes.length; i++) {
|
|
116
|
+
let { name, value, type } = envTypes[i]
|
|
117
|
+
// If an env-var wants to lookup itself, delegate straight to the parent
|
|
118
|
+
// https://github.com/node-red/node-red/issues/2099
|
|
119
|
+
if (value === name) {
|
|
120
|
+
value = `$parent.${name}`
|
|
121
|
+
}
|
|
122
|
+
if (evaluatedEnv.hasOwnProperty(value)) {
|
|
123
|
+
value = evaluatedEnv[value]
|
|
124
|
+
} else {
|
|
125
|
+
value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null);
|
|
126
|
+
}
|
|
127
|
+
evaluatedEnv[name] = value
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return evaluatedEnv
|
|
131
|
+
}
|
|
73
132
|
|
|
74
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Create a new instance of a node
|
|
135
|
+
* @param {Flow} flow The containing flow
|
|
136
|
+
* @param {object} config The node configuration object
|
|
137
|
+
* @return {Node} The instance of the node
|
|
138
|
+
*/
|
|
139
|
+
async function createNode(flow,config) {
|
|
75
140
|
var newNode = null;
|
|
76
141
|
var type = config.type;
|
|
77
142
|
try {
|
|
@@ -140,7 +205,7 @@ function createNode(flow,config) {
|
|
|
140
205
|
// This allows nodes inside the subflow to get ahold of each other
|
|
141
206
|
// such as a node accessing its config node
|
|
142
207
|
flow.subflowInstanceNodes[config.id] = subflow
|
|
143
|
-
subflow.start();
|
|
208
|
+
await subflow.start();
|
|
144
209
|
return subflow.node;
|
|
145
210
|
}
|
|
146
211
|
} catch(err) {
|
|
@@ -150,373 +215,343 @@ function createNode(flow,config) {
|
|
|
150
215
|
}
|
|
151
216
|
|
|
152
217
|
function parseConfig(config) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (n.type === 'subflow') {
|
|
177
|
-
flow.subflows[n.id] = n;
|
|
178
|
-
flow.subflows[n.id].configs = {};
|
|
179
|
-
flow.subflows[n.id].nodes = {};
|
|
180
|
-
flow.subflows[n.id].instances = [];
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
var linkWires = {};
|
|
184
|
-
var linkOutNodes = [];
|
|
185
|
-
config.forEach(function(n) {
|
|
186
|
-
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
|
|
187
|
-
var subflowDetails = subflowInstanceRE.exec(n.type);
|
|
188
|
-
|
|
189
|
-
if ( (subflowDetails && !flow.subflows[subflowDetails[1]]) || (!subflowDetails && !typeRegistry.get(n.type)) ) {
|
|
190
|
-
if (flow.missingTypes.indexOf(n.type) === -1) {
|
|
191
|
-
flow.missingTypes.push(n.type);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
var container = null;
|
|
195
|
-
if (flow.flows[n.z]) {
|
|
196
|
-
container = flow.flows[n.z];
|
|
197
|
-
} else if (flow.subflows[n.z]) {
|
|
198
|
-
container = flow.subflows[n.z];
|
|
199
|
-
}
|
|
200
|
-
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
|
|
201
|
-
if (subflowDetails) {
|
|
202
|
-
var subflowType = subflowDetails[1]
|
|
203
|
-
n.subflow = subflowType;
|
|
204
|
-
flow.subflows[subflowType].instances.push(n)
|
|
205
|
-
}
|
|
206
|
-
if (container) {
|
|
207
|
-
container.nodes[n.id] = n;
|
|
208
|
-
}
|
|
209
|
-
} else {
|
|
210
|
-
if (container) {
|
|
211
|
-
container.configs[n.id] = n;
|
|
212
|
-
} else {
|
|
213
|
-
flow.configs[n.id] = n;
|
|
214
|
-
flow.configs[n.id]._users = [];
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
if (n.type === 'link in' && n.links) {
|
|
218
|
-
// Ensure wires are present in corresponding link out nodes
|
|
219
|
-
n.links.forEach(function(id) {
|
|
220
|
-
linkWires[id] = linkWires[id]||{};
|
|
221
|
-
linkWires[id][n.id] = true;
|
|
222
|
-
})
|
|
223
|
-
} else if (n.type === 'link out' && n.links) {
|
|
224
|
-
linkWires[n.id] = linkWires[n.id]||{};
|
|
225
|
-
n.links.forEach(function(id) {
|
|
226
|
-
linkWires[n.id][id] = true;
|
|
227
|
-
})
|
|
228
|
-
linkOutNodes.push(n);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
linkOutNodes.forEach(function(n) {
|
|
233
|
-
var links = linkWires[n.id];
|
|
234
|
-
var targets = Object.keys(links);
|
|
235
|
-
n.wires = [targets];
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
var addedTabs = {};
|
|
240
|
-
config.forEach(function(n) {
|
|
241
|
-
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
|
|
242
|
-
for (var prop in n) {
|
|
243
|
-
if (n.hasOwnProperty(prop) && prop !== 'id' && prop !== 'wires' && prop !== 'type' && prop !== '_users' && flow.configs.hasOwnProperty(n[prop])) {
|
|
244
|
-
// This property references a global config node
|
|
245
|
-
flow.configs[n[prop]]._users.push(n.id)
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
if (n.z && !flow.subflows[n.z]) {
|
|
249
|
-
|
|
250
|
-
if (!flow.flows[n.z]) {
|
|
251
|
-
flow.flows[n.z] = {type:'tab',id:n.z};
|
|
252
|
-
flow.flows[n.z].subflows = {};
|
|
253
|
-
flow.flows[n.z].configs = {};
|
|
254
|
-
flow.flows[n.z].nodes = {};
|
|
255
|
-
addedTabs[n.z] = flow.flows[n.z];
|
|
256
|
-
}
|
|
257
|
-
if (addedTabs[n.z]) {
|
|
258
|
-
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
|
|
259
|
-
addedTabs[n.z].nodes[n.id] = n;
|
|
260
|
-
} else {
|
|
261
|
-
addedTabs[n.z].configs[n.id] = n;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
});
|
|
267
|
-
return flow;
|
|
268
|
-
}
|
|
218
|
+
var flow = {};
|
|
219
|
+
flow.allNodes = {};
|
|
220
|
+
flow.subflows = {};
|
|
221
|
+
flow.configs = {};
|
|
222
|
+
flow.flows = {};
|
|
223
|
+
flow.missingTypes = [];
|
|
224
|
+
|
|
225
|
+
config.forEach(function (n) {
|
|
226
|
+
flow.allNodes[n.id] = clone(n);
|
|
227
|
+
if (n.type === 'tab') {
|
|
228
|
+
flow.flows[n.id] = n;
|
|
229
|
+
flow.flows[n.id].subflows = {};
|
|
230
|
+
flow.flows[n.id].configs = {};
|
|
231
|
+
flow.flows[n.id].nodes = {};
|
|
232
|
+
flow.flows[n.id].groups = {};
|
|
233
|
+
} else if (n.type === 'subflow') {
|
|
234
|
+
flow.subflows[n.id] = n;
|
|
235
|
+
flow.subflows[n.id].configs = {};
|
|
236
|
+
flow.subflows[n.id].nodes = {};
|
|
237
|
+
flow.subflows[n.id].groups = {};
|
|
238
|
+
flow.subflows[n.id].instances = [];
|
|
239
|
+
}
|
|
240
|
+
});
|
|
269
241
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const cred = (gconf ? credentials.get(gconf.id) : null) || {
|
|
280
|
-
map: {}
|
|
281
|
-
};
|
|
282
|
-
const map = cred.map;
|
|
283
|
-
|
|
284
|
-
for (let i = 0; i < env.length; i++) {
|
|
285
|
-
const item = env[i];
|
|
286
|
-
if (item.name === name) {
|
|
287
|
-
if (item.type === "cred") {
|
|
288
|
-
return {
|
|
289
|
-
name: name,
|
|
290
|
-
value: map[name],
|
|
291
|
-
type: "cred"
|
|
292
|
-
};
|
|
242
|
+
var linkWires = {};
|
|
243
|
+
var linkOutNodes = [];
|
|
244
|
+
config.forEach(function (n) {
|
|
245
|
+
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
|
|
246
|
+
var subflowDetails = subflowInstanceRE.exec(n.type);
|
|
247
|
+
|
|
248
|
+
if ((subflowDetails && !flow.subflows[subflowDetails[1]]) || (!subflowDetails && !typeRegistry.get(n.type))) {
|
|
249
|
+
if (flow.missingTypes.indexOf(n.type) === -1) {
|
|
250
|
+
flow.missingTypes.push(n.type);
|
|
293
251
|
}
|
|
294
|
-
return item;
|
|
295
252
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
module.exports = {
|
|
302
|
-
init: function(runtime) {
|
|
303
|
-
_runtime = runtime;
|
|
304
|
-
envVarExcludes = {};
|
|
305
|
-
if (runtime.settings.hasOwnProperty('envVarExcludes') && Array.isArray(runtime.settings.envVarExcludes)) {
|
|
306
|
-
runtime.settings.envVarExcludes.forEach(v => envVarExcludes[v] = true);
|
|
307
|
-
}
|
|
308
|
-
},
|
|
309
|
-
getEnvVar: function(k) {
|
|
310
|
-
if (!envVarExcludes[k]) {
|
|
311
|
-
const item = getGlobalEnv(k);
|
|
312
|
-
if (item) {
|
|
313
|
-
const val = redUtil.evaluateNodeProperty(item.value, item.type, null, null, null);
|
|
314
|
-
return val;
|
|
253
|
+
var container = null;
|
|
254
|
+
if (flow.flows[n.z]) {
|
|
255
|
+
container = flow.flows[n.z];
|
|
256
|
+
} else if (flow.subflows[n.z]) {
|
|
257
|
+
container = flow.subflows[n.z];
|
|
315
258
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
flows:{},
|
|
335
|
-
allNodes:{}
|
|
259
|
+
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
|
|
260
|
+
if (subflowDetails) {
|
|
261
|
+
var subflowType = subflowDetails[1]
|
|
262
|
+
n.subflow = subflowType;
|
|
263
|
+
if (flow.subflows[subflowType]) {
|
|
264
|
+
flow.subflows[subflowType].instances.push(n)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (container) {
|
|
268
|
+
container.nodes[n.id] = n;
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
if (container) {
|
|
272
|
+
container.configs[n.id] = n;
|
|
273
|
+
} else {
|
|
274
|
+
flow.configs[n.id] = n;
|
|
275
|
+
flow.configs[n.id]._users = [];
|
|
276
|
+
}
|
|
336
277
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
278
|
+
if (n.type === 'link in' && n.links) {
|
|
279
|
+
// Ensure wires are present in corresponding link out nodes
|
|
280
|
+
n.links.forEach(function (id) {
|
|
281
|
+
linkWires[id] = linkWires[id] || {};
|
|
282
|
+
linkWires[id][n.id] = true;
|
|
283
|
+
})
|
|
284
|
+
} else if (n.type === 'link out' && n.links) {
|
|
285
|
+
linkWires[n.id] = linkWires[n.id] || {};
|
|
286
|
+
n.links.forEach(function (id) {
|
|
287
|
+
linkWires[n.id][id] = true;
|
|
288
|
+
})
|
|
289
|
+
linkOutNodes.push(n);
|
|
290
|
+
}
|
|
291
|
+
} else if (n.type === 'group') {
|
|
292
|
+
const parentContainer = flow.flows[n.z] || flow.subflows[n.z]
|
|
293
|
+
if (parentContainer) {
|
|
294
|
+
parentContainer.groups[n.id] = n
|
|
353
295
|
}
|
|
354
296
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
297
|
+
});
|
|
298
|
+
linkOutNodes.forEach(function (n) {
|
|
299
|
+
var links = linkWires[n.id];
|
|
300
|
+
var targets = Object.keys(links);
|
|
301
|
+
n.wires = [targets];
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
var addedTabs = {};
|
|
306
|
+
config.forEach(function (n) {
|
|
307
|
+
if (n.type !== 'subflow' && n.type !== 'tab' && n.type !== 'group') {
|
|
308
|
+
for (var prop in n) {
|
|
309
|
+
if (n.hasOwnProperty(prop) && prop !== 'id' && prop !== 'wires' && prop !== 'type' && prop !== '_users' && flow.configs.hasOwnProperty(n[prop])) {
|
|
310
|
+
// This property references a global config node
|
|
311
|
+
flow.configs[n[prop]]._users.push(n.id)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (n.z && !flow.subflows[n.z]) {
|
|
315
|
+
|
|
316
|
+
if (!flow.flows[n.z]) {
|
|
317
|
+
flow.flows[n.z] = { type: 'tab', id: n.z };
|
|
318
|
+
flow.flows[n.z].subflows = {};
|
|
319
|
+
flow.flows[n.z].configs = {};
|
|
320
|
+
flow.flows[n.z].nodes = {};
|
|
321
|
+
addedTabs[n.z] = flow.flows[n.z];
|
|
322
|
+
}
|
|
323
|
+
if (addedTabs[n.z]) {
|
|
324
|
+
if (n.hasOwnProperty('x') && n.hasOwnProperty('y')) {
|
|
325
|
+
addedTabs[n.z].nodes[n.id] = n;
|
|
365
326
|
} else {
|
|
366
|
-
|
|
327
|
+
addedTabs[n.z].configs[n.id] = n;
|
|
367
328
|
}
|
|
368
329
|
}
|
|
369
330
|
}
|
|
370
331
|
}
|
|
332
|
+
});
|
|
333
|
+
return flow;
|
|
334
|
+
}
|
|
335
|
+
function getEnvVar(k) {
|
|
336
|
+
if (!envVarExcludes[k]) {
|
|
337
|
+
return process.env[k];
|
|
338
|
+
}
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
function diffConfigs(oldConfig, newConfig) {
|
|
342
|
+
var id;
|
|
343
|
+
var node;
|
|
344
|
+
var nn;
|
|
345
|
+
var wires;
|
|
346
|
+
var j,k;
|
|
347
|
+
|
|
348
|
+
if (!oldConfig) {
|
|
349
|
+
oldConfig = {
|
|
350
|
+
flows:{},
|
|
351
|
+
allNodes:{}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
var changedSubflows = {};
|
|
355
|
+
|
|
356
|
+
var added = {};
|
|
357
|
+
var removed = {};
|
|
358
|
+
var changed = {};
|
|
359
|
+
var flowChanged = {};
|
|
360
|
+
var wiringChanged = {};
|
|
361
|
+
var globalConfigChanged = false;
|
|
362
|
+
var linkMap = {};
|
|
363
|
+
var allNestedGroups = []
|
|
364
|
+
|
|
365
|
+
// Look for tabs that have been removed
|
|
366
|
+
for (id in oldConfig.flows) {
|
|
367
|
+
if (oldConfig.flows.hasOwnProperty(id) && (!newConfig.flows.hasOwnProperty(id))) {
|
|
368
|
+
removed[id] = oldConfig.allNodes[id];
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
371
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
372
|
+
// Look for tabs that have been disabled
|
|
373
|
+
for (id in oldConfig.flows) {
|
|
374
|
+
if (oldConfig.flows.hasOwnProperty(id) && newConfig.flows.hasOwnProperty(id)) {
|
|
375
|
+
var originalState = oldConfig.flows[id].disabled||false;
|
|
376
|
+
var newState = newConfig.flows[id].disabled||false;
|
|
377
|
+
if (originalState !== newState) {
|
|
378
|
+
if (originalState) {
|
|
379
|
+
added[id] = oldConfig.allNodes[id];
|
|
380
|
+
} else {
|
|
381
|
+
removed[id] = oldConfig.allNodes[id];
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
for (id in oldConfig.allNodes) {
|
|
388
|
+
if (oldConfig.allNodes.hasOwnProperty(id)) {
|
|
389
|
+
node = oldConfig.allNodes[id];
|
|
390
|
+
if (node.type !== 'tab') {
|
|
391
|
+
// build the map of what this node was previously wired to
|
|
392
|
+
if (node.wires) {
|
|
393
|
+
linkMap[node.id] = linkMap[node.id] || [];
|
|
394
|
+
for (j=0;j<node.wires.length;j++) {
|
|
395
|
+
wires = node.wires[j];
|
|
396
|
+
for (k=0;k<wires.length;k++) {
|
|
397
|
+
linkMap[node.id].push(wires[k]);
|
|
398
|
+
nn = oldConfig.allNodes[wires[k]];
|
|
399
|
+
if (nn) {
|
|
400
|
+
linkMap[nn.id] = linkMap[nn.id] || [];
|
|
401
|
+
linkMap[nn.id].push(node.id);
|
|
388
402
|
}
|
|
389
403
|
}
|
|
390
404
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
405
|
+
}
|
|
406
|
+
// This node has been removed or its flow disabled
|
|
407
|
+
if (removed[node.z] || !newConfig.allNodes.hasOwnProperty(id)) {
|
|
408
|
+
removed[id] = node;
|
|
409
|
+
// Mark the container as changed
|
|
410
|
+
if (!removed[node.z] && newConfig.allNodes[removed[id].z]) {
|
|
411
|
+
changed[removed[id].z] = newConfig.allNodes[removed[id].z];
|
|
412
|
+
if (changed[removed[id].z].type === "subflow") {
|
|
413
|
+
changedSubflows[removed[id].z] = changed[removed[id].z];
|
|
414
|
+
//delete removed[id];
|
|
401
415
|
}
|
|
416
|
+
}
|
|
417
|
+
} else {
|
|
418
|
+
if (added[node.z]) {
|
|
419
|
+
added[id] = node;
|
|
402
420
|
} else {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
421
|
+
var currentState = node.d;
|
|
422
|
+
var newState = newConfig.allNodes[id].d;
|
|
423
|
+
if (!currentState && newState) {
|
|
424
|
+
removed[id] = node;
|
|
425
|
+
}
|
|
426
|
+
// This node has a material configuration change
|
|
427
|
+
if (diffNodes(node,newConfig.allNodes[id]) || newConfig.allNodes[id].credentials) {
|
|
428
|
+
changed[id] = newConfig.allNodes[id];
|
|
429
|
+
if (changed[id].type === "subflow") {
|
|
430
|
+
changedSubflows[id] = changed[id];
|
|
410
431
|
}
|
|
411
|
-
//
|
|
412
|
-
if (
|
|
413
|
-
changed[id] = newConfig.allNodes[id];
|
|
414
|
-
if (changed[id].type === "subflow") {
|
|
415
|
-
changedSubflows[id] = changed[id];
|
|
416
|
-
|
|
417
|
-
// Mark the container as changed
|
|
418
|
-
if (newConfig.allNodes[changed[id].z]) {
|
|
419
|
-
changed[changed[id].z] = newConfig.allNodes[changed[id].z];
|
|
420
|
-
if (changed[changed[id].z].type === "subflow") {
|
|
421
|
-
changedSubflows[changed[id].z] = changed[changed[id].z];
|
|
422
|
-
delete changed[id];
|
|
423
|
-
}
|
|
432
|
+
// Mark the container as changed
|
|
433
|
+
if (newConfig.allNodes[changed[id].z]) {
|
|
434
|
+
changed[changed[id].z] = newConfig.allNodes[changed[id].z];
|
|
435
|
+
if (changed[changed[id].z].type === "subflow") {
|
|
436
|
+
changedSubflows[changed[id].z] = changed[changed[id].z];
|
|
437
|
+
delete changed[id];
|
|
424
438
|
}
|
|
425
439
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
440
|
+
if (newConfig.allNodes[id].type === 'global-config') {
|
|
441
|
+
globalConfigChanged = true
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
// This node's wiring has changed
|
|
445
|
+
if (!redUtil.compareObjects(node.wires,newConfig.allNodes[id].wires)) {
|
|
446
|
+
wiringChanged[id] = newConfig.allNodes[id];
|
|
447
|
+
// Mark the container as changed
|
|
448
|
+
if (newConfig.allNodes[wiringChanged[id].z]) {
|
|
449
|
+
changed[wiringChanged[id].z] = newConfig.allNodes[wiringChanged[id].z];
|
|
450
|
+
if (changed[wiringChanged[id].z].type === "subflow") {
|
|
451
|
+
changedSubflows[wiringChanged[id].z] = changed[wiringChanged[id].z];
|
|
452
|
+
delete wiringChanged[id];
|
|
436
453
|
}
|
|
437
454
|
}
|
|
438
455
|
}
|
|
439
456
|
}
|
|
440
457
|
}
|
|
458
|
+
} else if (!removed[id]) {
|
|
459
|
+
if (JSON.stringify(node.env) !== JSON.stringify(newConfig.allNodes[id].env)) {
|
|
460
|
+
flowChanged[id] = newConfig.allNodes[id];
|
|
461
|
+
}
|
|
441
462
|
}
|
|
442
463
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
464
|
+
}
|
|
465
|
+
// Look for added nodes
|
|
466
|
+
for (id in newConfig.allNodes) {
|
|
467
|
+
if (newConfig.allNodes.hasOwnProperty(id)) {
|
|
468
|
+
node = newConfig.allNodes[id];
|
|
469
|
+
if (node.type === 'group') {
|
|
470
|
+
if (node.g) {
|
|
471
|
+
allNestedGroups.push(node)
|
|
472
|
+
}
|
|
473
|
+
if (changed[node.id]) {
|
|
474
|
+
if (node.nodes) {
|
|
475
|
+
node.nodes.forEach(nid => {
|
|
476
|
+
if (!changed[nid]) {
|
|
477
|
+
changed[nid] = true
|
|
455
478
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
479
|
+
})
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
// build the map of what this node is now wired to
|
|
484
|
+
if (node.wires) {
|
|
485
|
+
linkMap[node.id] = linkMap[node.id] || [];
|
|
486
|
+
for (j=0;j<node.wires.length;j++) {
|
|
487
|
+
wires = node.wires[j];
|
|
488
|
+
for (k=0;k<wires.length;k++) {
|
|
489
|
+
if (linkMap[node.id].indexOf(wires[k]) === -1) {
|
|
490
|
+
linkMap[node.id].push(wires[k]);
|
|
491
|
+
}
|
|
492
|
+
nn = newConfig.allNodes[wires[k]];
|
|
493
|
+
if (nn) {
|
|
494
|
+
linkMap[nn.id] = linkMap[nn.id] || [];
|
|
495
|
+
if (linkMap[nn.id].indexOf(node.id) === -1) {
|
|
496
|
+
linkMap[nn.id].push(node.id);
|
|
462
497
|
}
|
|
463
498
|
}
|
|
464
499
|
}
|
|
465
500
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
501
|
+
}
|
|
502
|
+
// This node has been added
|
|
503
|
+
if (!oldConfig.allNodes.hasOwnProperty(id)) {
|
|
504
|
+
added[id] = node;
|
|
505
|
+
// Mark the container as changed
|
|
506
|
+
if (newConfig.allNodes[added[id].z]) {
|
|
507
|
+
changed[added[id].z] = newConfig.allNodes[added[id].z];
|
|
508
|
+
if (changed[added[id].z].type === "subflow") {
|
|
509
|
+
changedSubflows[added[id].z] = changed[added[id].z];
|
|
510
|
+
delete added[id];
|
|
476
511
|
}
|
|
477
512
|
}
|
|
478
513
|
}
|
|
479
514
|
}
|
|
515
|
+
}
|
|
480
516
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
}
|
|
517
|
+
var madeChange;
|
|
518
|
+
// Loop through the nodes looking for references to changed config nodes
|
|
519
|
+
// Repeat the loop if anything is marked as changed as it may need to be
|
|
520
|
+
// propagated to parent nodes.
|
|
521
|
+
// TODO: looping through all nodes every time is a bit inefficient - could be more targeted
|
|
522
|
+
do {
|
|
523
|
+
madeChange = false;
|
|
524
|
+
for (id in newConfig.allNodes) {
|
|
525
|
+
if (newConfig.allNodes.hasOwnProperty(id)) {
|
|
526
|
+
node = newConfig.allNodes[id];
|
|
527
|
+
for (var prop in node) {
|
|
528
|
+
if (node.hasOwnProperty(prop) && prop != "z" && prop != "id" && prop != "wires") {
|
|
529
|
+
// This node has a property that references a changed/removed node
|
|
530
|
+
// Assume it is a config node change and mark this node as
|
|
531
|
+
// changed.
|
|
532
|
+
|
|
533
|
+
var changeOrigin = changed[node[prop]];
|
|
534
|
+
if (changeOrigin || removed[node[prop]]) {
|
|
535
|
+
if (!changed[node.id]) {
|
|
536
|
+
if (changeOrigin &&
|
|
537
|
+
(prop === "g") &&
|
|
538
|
+
(changeOrigin.type === "group")) {
|
|
539
|
+
var oldNode = oldConfig.allNodes[node.id];
|
|
540
|
+
// ignore change of group node
|
|
541
|
+
// if group of this node not changed
|
|
542
|
+
if (oldNode &&
|
|
543
|
+
(node.g === oldNode.g)) {
|
|
544
|
+
continue;
|
|
510
545
|
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
546
|
+
}
|
|
547
|
+
madeChange = true;
|
|
548
|
+
changed[node.id] = node;
|
|
549
|
+
// This node exists within subflow template
|
|
550
|
+
// Mark the template as having changed
|
|
551
|
+
if (newConfig.allNodes[node.z]) {
|
|
552
|
+
changed[node.z] = newConfig.allNodes[node.z];
|
|
553
|
+
if (changed[node.z].type === "subflow") {
|
|
554
|
+
changedSubflows[node.z] = changed[node.z];
|
|
520
555
|
}
|
|
521
556
|
}
|
|
522
557
|
}
|
|
@@ -524,94 +559,123 @@ module.exports = {
|
|
|
524
559
|
}
|
|
525
560
|
}
|
|
526
561
|
}
|
|
527
|
-
}
|
|
562
|
+
}
|
|
563
|
+
} while (madeChange===true)
|
|
564
|
+
|
|
565
|
+
// Find any nodes that exist on a subflow template and remove from changed
|
|
566
|
+
// list as the parent subflow will now be marked as containing a change
|
|
567
|
+
for (id in newConfig.allNodes) {
|
|
568
|
+
if (newConfig.allNodes.hasOwnProperty(id)) {
|
|
569
|
+
node = newConfig.allNodes[id];
|
|
570
|
+
if (newConfig.allNodes[node.z] && newConfig.allNodes[node.z].type === "subflow") {
|
|
571
|
+
delete changed[node.id];
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
528
575
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
576
|
+
// Recursively mark all children of changed groups as changed
|
|
577
|
+
do {
|
|
578
|
+
madeChange = false
|
|
579
|
+
for (let i = 0; i < allNestedGroups.length; i++) {
|
|
580
|
+
const group = allNestedGroups[i]
|
|
581
|
+
if (!changed[group.id] && group.g && changed[group.g]) {
|
|
582
|
+
changed[group.id] = true
|
|
583
|
+
madeChange = true
|
|
584
|
+
}
|
|
585
|
+
if (changed[group.id] && group.nodes) {
|
|
586
|
+
group.nodes.forEach(nid => {
|
|
587
|
+
if (!changed[nid]) {
|
|
588
|
+
changed[nid] = true
|
|
589
|
+
madeChange = true
|
|
590
|
+
}
|
|
591
|
+
})
|
|
537
592
|
}
|
|
538
593
|
}
|
|
594
|
+
} while(madeChange)
|
|
539
595
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
}
|
|
596
|
+
// Recursively mark all instances of changed subflows as changed
|
|
597
|
+
var changedSubflowStack = Object.keys(changedSubflows);
|
|
598
|
+
while (changedSubflowStack.length > 0) {
|
|
599
|
+
var subflowId = changedSubflowStack.pop();
|
|
600
|
+
for (id in newConfig.allNodes) {
|
|
601
|
+
if (newConfig.allNodes.hasOwnProperty(id)) {
|
|
602
|
+
node = newConfig.allNodes[id];
|
|
603
|
+
if (node.type === 'subflow:'+subflowId) {
|
|
604
|
+
if (!changed[node.id]) {
|
|
605
|
+
changed[node.id] = node;
|
|
606
|
+
if (!changed[changed[node.id].z] && newConfig.allNodes[changed[node.id].z]) {
|
|
607
|
+
changed[changed[node.id].z] = newConfig.allNodes[changed[node.id].z];
|
|
608
|
+
if (newConfig.allNodes[changed[node.id].z].type === "subflow") {
|
|
609
|
+
// This subflow instance is inside a subflow. Add the
|
|
610
|
+
// containing subflow to the stack to mark
|
|
611
|
+
changedSubflowStack.push(changed[node.id].z);
|
|
612
|
+
delete changed[node.id];
|
|
558
613
|
}
|
|
559
614
|
}
|
|
560
615
|
}
|
|
561
616
|
}
|
|
562
617
|
}
|
|
563
618
|
}
|
|
619
|
+
}
|
|
564
620
|
|
|
565
|
-
var diff = {
|
|
566
|
-
added:Object.keys(added),
|
|
567
|
-
changed:Object.keys(changed),
|
|
568
|
-
removed:Object.keys(removed),
|
|
569
|
-
rewired:Object.keys(wiringChanged),
|
|
570
|
-
linked:[]
|
|
571
|
-
}
|
|
572
621
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
622
|
+
|
|
623
|
+
var diff = {
|
|
624
|
+
added:Object.keys(added),
|
|
625
|
+
changed:Object.keys(changed),
|
|
626
|
+
removed:Object.keys(removed),
|
|
627
|
+
rewired:Object.keys(wiringChanged),
|
|
628
|
+
linked:[],
|
|
629
|
+
flowChanged: Object.keys(flowChanged),
|
|
630
|
+
globalConfigChanged
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Traverse the links of all modified nodes to mark the connected nodes
|
|
634
|
+
var modifiedNodes = diff.added.concat(diff.changed).concat(diff.removed).concat(diff.rewired);
|
|
635
|
+
var visited = {};
|
|
636
|
+
while (modifiedNodes.length > 0) {
|
|
637
|
+
node = modifiedNodes.pop();
|
|
638
|
+
if (!visited[node]) {
|
|
639
|
+
visited[node] = true;
|
|
640
|
+
if (linkMap[node]) {
|
|
641
|
+
if (!changed[node] && !added[node] && !removed[node] && !wiringChanged[node]) {
|
|
642
|
+
diff.linked.push(node);
|
|
585
643
|
}
|
|
644
|
+
modifiedNodes = modifiedNodes.concat(linkMap[node]);
|
|
586
645
|
}
|
|
587
646
|
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
647
|
+
}
|
|
648
|
+
// console.log(diff);
|
|
649
|
+
// for (id in newConfig.allNodes) {
|
|
650
|
+
// if (added[id] || changed[id] || wiringChanged[id] || diff.linked.indexOf(id)!==-1) {
|
|
651
|
+
// console.log(
|
|
652
|
+
// (added[id]?"a":(changed[id]?"c":" "))+(wiringChanged[id]?"w":" ")+(diff.linked.indexOf(id)!==-1?"l":" "),
|
|
653
|
+
// newConfig.allNodes[id].type.padEnd(10),
|
|
654
|
+
// id.padEnd(16),
|
|
655
|
+
// (newConfig.allNodes[id].z||"").padEnd(16),
|
|
656
|
+
// newConfig.allNodes[id].name||newConfig.allNodes[id].label||""
|
|
657
|
+
// );
|
|
658
|
+
// }
|
|
659
|
+
// }
|
|
660
|
+
// for (id in removed) {
|
|
661
|
+
// console.log(
|
|
662
|
+
// "- "+(diff.linked.indexOf(id)!==-1?"~":" "),
|
|
663
|
+
// id,
|
|
664
|
+
// oldConfig.allNodes[id].type,
|
|
665
|
+
// oldConfig.allNodes[id].name||oldConfig.allNodes[id].label||""
|
|
666
|
+
// );
|
|
667
|
+
// }
|
|
668
|
+
|
|
669
|
+
return diff;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
module.exports = {
|
|
673
|
+
init,
|
|
674
|
+
createNode,
|
|
675
|
+
parseConfig,
|
|
676
|
+
diffConfigs,
|
|
677
|
+
diffNodes,
|
|
678
|
+
getEnvVar,
|
|
679
|
+
mapEnvVarProperties,
|
|
680
|
+
evaluateEnvProperties
|
|
617
681
|
}
|