Problem
When using types relying on native bindings, such as tf.tensor
from TensorFlowLite, the standard clone functionality provided by Node-RED is not capable of deep cloning, meaning that only the first output of each node can be used reliably.
Workaround
One possible solution is that a Node library that uses such types and requires the message to be cloned also provides a custom cloner node taking one message as input, does a custom deep cloning on 2 or more outputs. The drawback of such approach is that all Nodes with multiple outputs cannot be used to process such messages, and they functionality must be duplicated, likely using function nodes.
Proposal
My proposal is to provide the possibility to define custom cloners for given Javascript types, (identified by their prototype) which are not supported by lodash.cloneDeep
. I propose to use lodash.cloneDeepWith
to perform the cloning.
Sample code:
The var m = cloneDeep(msg)
in RED.utils.cloneMessage
is replaced by
var m = cloneDeepWith(msg, function(v) {
const cloner = customClonerMap.get(Object.getPrototypeOf(v));
return cloner ? cloner(v) : undefined;
})
Where customClonerMap
is a Map
containing the cloner functions associated to each prototype.
cloneDeepWith
recursively clones the object using the function. In case the return value of the cloner function is undefined
the standard lodash.clone
is performed.
How to register a cloner
1. Nodes register their own cloner
Nodes that want to register a custom cloner do so by calling RED.utils.registerCloner(type, cloner)
which register the cloner function for the type.prototype
(where type
is a Function
).
Multiple nodes might register a cloner for the same type: in this case the latter cloner registered with a call to registerCloner
would be one used. Since the goal of the cloner function is to correctly clone the specific type, it should not matter which is the actual cloner function that will be used since it should correctly clone the given type. This is also needed in order to allow for multiple nodes in a same contribution to register the same cloner as any of the nodes can be added to the flow.
My main concern with this approach is that there is no effective way to prevent or warn the user when a malicious node register a malicious cloner function. Message passing and cloning is at the core of the trust user have in Node-RED and I do not like the eventuality that such trust might be compromised.
2. Users add cloner via a specific configuration node (preferred)
One alternative solution would be specify a new type of node, similar to config nodes, that is global to the Node-RED instance and whose task is only to register the custom cloner for a given type. In this case, it would be responsibility of the user to add that specific node, and Node-RED could enforce that only one such node per type can be used.
Normal nodes should be able to check that the required cloner nodes have been added to the flow.
If a cloner configuration node required by a node in the flow is missing, the flow cannot be deployed.
Eager to read what the Node-RED community thinks!