@node-red/runtime
Advanced tools
Comparing version 0.20.0-beta.4 to 0.20.0-beta.5
@@ -21,2 +21,3 @@ /** | ||
var memory = require("./memory"); | ||
var flows; | ||
@@ -51,2 +52,3 @@ var settings; | ||
function init(_settings) { | ||
flows = require("../flows"); | ||
settings = _settings; | ||
@@ -203,4 +205,20 @@ contexts = {}; | ||
function followParentContext(parent, key) { | ||
if (key === "$parent") { | ||
return [parent, undefined]; | ||
} | ||
else if (key.startsWith("$parent.")) { | ||
var len = "$parent.".length; | ||
var new_key = key.substring(len); | ||
var ctx = parent; | ||
while (ctx && new_key.startsWith("$parent.")) { | ||
ctx = ctx.$parent; | ||
new_key = new_key.substring(len); | ||
} | ||
return [ctx, new_key]; | ||
} | ||
return null; | ||
} | ||
function createContext(id,seed) { | ||
function createContext(id,seed,parent) { | ||
// Seed is only set for global context - sourced from functionGlobalContext | ||
@@ -260,2 +278,17 @@ var scope = id; | ||
} | ||
var result = followParentContext(parent, key); | ||
if (result) { | ||
var [ctx, new_key] = result; | ||
if (ctx && new_key) { | ||
return ctx.get(new_key, storage, callback); | ||
} | ||
else { | ||
if (callback) { | ||
return callback(undefined); | ||
} | ||
else { | ||
return undefined; | ||
} | ||
} | ||
} | ||
} else { | ||
@@ -328,2 +361,15 @@ if (!storage) { | ||
} | ||
var result = followParentContext(parent, key); | ||
if (result) { | ||
var [ctx, new_key] = result; | ||
if (ctx && new_key) { | ||
return ctx.set(new_key, value, storage, callback); | ||
} | ||
else { | ||
if (callback) { | ||
return callback(); | ||
} | ||
return undefined; | ||
} | ||
} | ||
} else { | ||
@@ -369,6 +415,32 @@ if (!storage) { | ||
}); | ||
if (parent) { | ||
Object.defineProperty(obj, "$parent", { | ||
value: parent | ||
}); | ||
} | ||
return obj; | ||
} | ||
function getContext(localId,flowId) { | ||
function createRootContext() { | ||
var obj = {}; | ||
Object.defineProperties(obj, { | ||
get: { | ||
value: function(key, storage, callback) { | ||
return undefined; | ||
} | ||
}, | ||
set: { | ||
value: function(key, value, storage, callback) { | ||
} | ||
}, | ||
keys: { | ||
value: function(storage, callback) { | ||
return undefined; | ||
} | ||
} | ||
}); | ||
return obj; | ||
} | ||
function getContext(localId,flowId,parent) { | ||
var contextId = localId; | ||
@@ -381,6 +453,15 @@ if (flowId) { | ||
} | ||
var newContext = createContext(contextId); | ||
var newContext = createContext(contextId,undefined,parent); | ||
if (flowId) { | ||
var node = flows.get(flowId); | ||
var parent = undefined; | ||
if (node && node.type.startsWith("subflow:")) { | ||
parent = node.context().flow; | ||
} | ||
else { | ||
parent = createRootContext(); | ||
} | ||
var flowContext = getContext(flowId,undefined,parent); | ||
Object.defineProperty(newContext, 'flow', { | ||
value: getContext(flowId) | ||
value: flowContext | ||
}); | ||
@@ -387,0 +468,0 @@ } |
@@ -215,2 +215,17 @@ /** | ||
} | ||
this.catchNodes.sort(function(A,B) { | ||
if (A.scope && !B.scope) { | ||
return -1; | ||
} else if (!A.scope && B.scope) { | ||
return 1; | ||
} else if (A.scope && B.scope) { | ||
return 0; | ||
} else if (A.uncaught && !B.uncaught) { | ||
return 1; | ||
} else if (!A.uncaught && B.uncaught) { | ||
return -1; | ||
} | ||
return 0; | ||
}); | ||
if (activeCount > 0) { | ||
@@ -269,3 +284,2 @@ this.trace("------------------|--------------|-----------------"); | ||
/** | ||
@@ -286,7 +300,9 @@ * Update the flow definition. This doesn't change anything that is running. | ||
* flow, pass the request up to the parent. | ||
* @param {[type]} id [description] | ||
* @param {String} id [description] | ||
* @param {Boolean} cancelBubble if true, prevents the flow from passing the request to the parent | ||
* This stops infinite loops when the parent asked this Flow for the | ||
* node to begin with. | ||
* @return {[type]} [description] | ||
*/ | ||
getNode(id) { | ||
// console.log('getNode',id,!!this.activeNodes[id]) | ||
getNode(id, cancelBubble) { | ||
if (!id) { | ||
@@ -304,3 +320,6 @@ return undefined; | ||
} | ||
return this.parent.getNode(id); | ||
if (!cancelBubble) { | ||
return this.parent.getNode(id); | ||
} | ||
return undefined; | ||
} | ||
@@ -422,2 +441,4 @@ | ||
} else { | ||
var handledByUncaught = false; | ||
this.catchNodes.forEach(function(targetCatchNode) { | ||
@@ -427,2 +448,10 @@ if (targetCatchNode.scope && targetCatchNode.scope.indexOf(reportingNode.id) === -1) { | ||
} | ||
if (!targetCatchNode.scope && targetCatchNode.uncaught && !handledByUncaught) { | ||
if (handled) { | ||
// This has been handled by a !uncaught catch node | ||
return; | ||
} | ||
// This is an uncaught error | ||
handledByUncaught = true; | ||
} | ||
var errorMessage; | ||
@@ -470,2 +499,3 @@ if (msg) { | ||
} | ||
} | ||
@@ -472,0 +502,0 @@ |
@@ -210,7 +210,7 @@ /** | ||
if (activeNodesToFlow[id] && activeFlows[activeNodesToFlow[id]]) { | ||
return activeFlows[activeNodesToFlow[id]].getNode(id); | ||
return activeFlows[activeNodesToFlow[id]].getNode(id,true); | ||
} | ||
for (var flowId in activeFlows) { | ||
if (activeFlows.hasOwnProperty(flowId)) { | ||
node = activeFlows[flowId].getNode(id); | ||
node = activeFlows[flowId].getNode(id,true); | ||
if (node) { | ||
@@ -217,0 +217,0 @@ return node; |
@@ -20,2 +20,4 @@ /** | ||
const util = require("util"); | ||
const redUtil = require("@node-red/util").util; | ||
@@ -26,2 +28,3 @@ const flowUtil = require("./util"); | ||
/** | ||
@@ -42,3 +45,2 @@ * This class represents a subflow - which is handled as a special type of Flow | ||
constructor(parent,globalFlow,subflowDef,subflowInstance) { | ||
// console.log(subflowDef); | ||
// console.log("CREATE SUBFLOW",subflowDef.id,subflowInstance.id); | ||
@@ -96,2 +98,11 @@ // console.log("SubflowInstance\n"+JSON.stringify(subflowInstance," ",2)); | ||
this.node_map = node_map; | ||
var env = []; | ||
if (this.subflowDef.env) { | ||
this.subflowDef.env.forEach(e => { env[e.name] = e; }); | ||
} | ||
if (this.subflowInstance.env) { | ||
this.subflowInstance.env.forEach(e => { env[e.name] = e; }); | ||
} | ||
this.env = env; | ||
} | ||
@@ -111,2 +122,36 @@ | ||
var Node = require("../Node"); | ||
if (this.subflowDef.status) { | ||
var subflowStatusConfig = { | ||
id: this.subflowInstance.id+":status", | ||
type: "subflow-status", | ||
z: this.subflowInstance.id, | ||
_flow: this.parent | ||
} | ||
this.statusNode = new Node(subflowStatusConfig); | ||
this.statusNode.on("input", function(msg) { | ||
if (msg.payload !== undefined) { | ||
if (typeof msg.payload === "string") { | ||
// if msg.payload is a String, use it as status text | ||
self.node.status({text:msg.payload}) | ||
return; | ||
} else if (Object.prototype.toString.call(msg.payload) === "[object Object]") { | ||
if (msg.payload.hasOwnProperty('text') || msg.payload.hasOwnProperty('fill') || msg.payload.hasOwnProperty('shape') || Object.keys(msg.payload).length === 0) { | ||
// msg.payload is an object that looks like a status object | ||
self.node.status(msg.payload); | ||
return; | ||
} | ||
} | ||
// Anything else - inspect it and use as status text | ||
var text = util.inspect(msg.payload); | ||
if (text.length > 32) { text = text.substr(0,32) + "..."; } | ||
self.node.status({text:text}); | ||
} else if (msg.status !== undefined) { | ||
// if msg.status exists | ||
self.node.status(msg.status) | ||
} | ||
}) | ||
} | ||
var subflowInstanceConfig = { | ||
@@ -172,11 +217,10 @@ id: this.subflowInstance.id, | ||
} | ||
} | ||
}; | ||
// Wire the subflow outputs | ||
if (this.subflowDef.out) { | ||
var modifiedNodes = {}; | ||
for (var i=0;i<this.subflowDef.out.length;i++) { | ||
// i: the output index | ||
// This is what this Output is wired to | ||
wires = this.subflowDef.out[i].wires; | ||
var wires = this.subflowDef.out[i].wires; | ||
for (var j=0;j<wires.length;j++) { | ||
@@ -189,3 +233,2 @@ if (wires[j].id === this.subflowDef.id) { | ||
var node = self.node_map[wires[j].id]; | ||
modifiedNodes[node.id] = node; | ||
if (!node._originalWires) { | ||
@@ -199,2 +242,22 @@ node._originalWires = clone(node.wires); | ||
} | ||
if (this.subflowDef.status) { | ||
var subflowStatusId = this.statusNode.id; | ||
wires = this.subflowDef.status.wires; | ||
for (var j=0;j<wires.length;j++) { | ||
if (wires[j].id === this.subflowDef.id) { | ||
// A subflow input wired straight to a subflow output | ||
subflowInstanceConfig.wires[wires[j].port].push(subflowStatusId); | ||
this.node._updateWires(subflowInstanceConfig.wires); | ||
} else { | ||
var node = self.node_map[wires[j].id]; | ||
if (!node._originalWires) { | ||
node._originalWires = clone(node.wires); | ||
} | ||
node.wires[wires[j].port] = (node.wires[wires[j].port]||[]); | ||
node.wires[wires[j].port].push(subflowStatusId); | ||
} | ||
} | ||
} | ||
super.start(diff); | ||
@@ -204,2 +267,45 @@ } | ||
/** | ||
* Get environment variable of subflow | ||
* @param {String} name name of env var | ||
* @return {Object} val value of env var | ||
*/ | ||
getSetting(name) { | ||
var env = this.env; | ||
if (env && env.hasOwnProperty(name)) { | ||
var val = env[name]; | ||
try { | ||
var ret = redUtil.evaluateNodeProperty(val.value, val.type, this.node, null, null); | ||
return ret; | ||
} | ||
catch (e) { | ||
this.error(e); | ||
return undefined; | ||
} | ||
} | ||
var parent = this.parent; | ||
if (parent) { | ||
var val = parent.getSetting(name); | ||
return val; | ||
} | ||
return undefined; | ||
} | ||
/** | ||
* Get a node instance from this subflow. | ||
* If the subflow has a status node, check for that, otherwise use | ||
* the super-class function | ||
* @param {String} id [description] | ||
* @param {Boolean} cancelBubble if true, prevents the flow from passing the request to the parent | ||
* This stops infinite loops when the parent asked this Flow for the | ||
* node to begin with. | ||
* @return {[type]} [description] | ||
*/ | ||
getNode(id, cancelBubble) { | ||
if (this.statusNode && this.statusNode.id === id) { | ||
return this.statusNode; | ||
} | ||
return super.getNode(id,cancelBubble); | ||
} | ||
/** | ||
* Handle a status event from a node within this flow. | ||
@@ -217,6 +323,10 @@ * @param {Node} node The original node that triggered the event | ||
if (!handled) { | ||
// No status node on this subflow caught the status message. | ||
// Pass up to the parent with this subflow's instance as the | ||
// reporting node | ||
handled = this.parent.handleStatus(node,statusMessage,this.node,true); | ||
if (!this.statusNode || node === this.node) { | ||
// No status node on this subflow caught the status message. | ||
// AND there is no Subflow Status node - so the user isn't | ||
// wanting to manage status messages themselves | ||
// Pass up to the parent with this subflow's instance as the | ||
// reporting node | ||
handled = this.parent.handleStatus(node,statusMessage,this.node,true); | ||
} | ||
} | ||
@@ -247,3 +357,2 @@ return handled; | ||
} | ||
@@ -250,0 +359,0 @@ |
@@ -41,3 +41,7 @@ /** | ||
// the object (such as dashboard) will not like circular refs | ||
Object.defineProperty(this,'_flow', {value: n._flow, }) | ||
// The value must still be writable in the case that a node does: | ||
// Object.assign(this,config) | ||
// as part of its constructure - config._flow will overwrite this._flow | ||
// which we can tolerate as they are the same object. | ||
Object.defineProperty(this,'_flow', {value: n._flow, enumerable: false, writable: true }) | ||
} | ||
@@ -44,0 +48,0 @@ this.updateWires(n.wires); |
{ | ||
"name": "@node-red/runtime", | ||
"version": "0.20.0-beta.4", | ||
"version": "0.20.0-beta.5", | ||
"license": "Apache-2.0", | ||
@@ -19,4 +19,4 @@ "main": "./lib/index.js", | ||
"dependencies": { | ||
"@node-red/registry": "0.20.0-beta.4", | ||
"@node-red/util": "0.20.0-beta.4", | ||
"@node-red/registry": "0.20.0-beta.5", | ||
"@node-red/util": "0.20.0-beta.5", | ||
"clone": "2.1.2", | ||
@@ -23,0 +23,0 @@ "express": "4.16.4", |
424669
10372
+ Added@node-red/registry@0.20.0-beta.5(transitive)
+ Added@node-red/util@0.20.0-beta.5(transitive)
+ Addedi18next@14.1.1(transitive)
- Removed@node-red/registry@0.20.0-beta.4(transitive)
- Removed@node-red/util@0.20.0-beta.4(transitive)
- Removedi18next@13.1.0(transitive)
Updated@node-red/util@0.20.0-beta.5