From eab512ef22191f3dc6418c4c2da0313bf9419170 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:55:23 +0200 Subject: [PATCH 001/121] Fix save and history of credentials for panes --- .../src/js/ui/editors/panes/properties.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js index cfa72be10..9b18ec35e 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js @@ -131,9 +131,13 @@ } } if (node._def.credentials) { - var credDefinition = node._def.credentials; - var credsChanged = updateNodeCredentials(node,credDefinition,this.inputClass); - editState.changed = editState.changed || credsChanged; + const credsDefinition = node._def.credentials; + const credsChanged = updateNodeCredentials(node, credsDefinition, this.inputClass); + + if (credsChanged) { + editState.changed = true; + editState.changes.credentials = node.credentials._; + } } } } @@ -178,11 +182,14 @@ var value = input.val(); if (credDefinition[cred].type == 'password') { node.credentials['has_' + cred] = (value !== ""); - if (value == '__PWRD__') { + + // Skip if the credential has not changed + if ((value === '__PWRD__' && node.credentials._['has_' + cred] === true) || + (value === "" && node.credentials._['has_' + cred] === false)) { continue; } - changed = true; + changed = true; } node.credentials[cred] = value; if (value != node.credentials._[cred]) { @@ -193,6 +200,4 @@ } return changed; } - - })(); From 53e092e4840ae03ef53ea0cc461096d3dc553cb5 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Tue, 25 Jun 2024 15:06:58 +0200 Subject: [PATCH 002/121] Add config node to history + handling `changed` prop --- .../editor-client/src/js/ui/editor.js | 231 ++++++++++-------- 1 file changed, 134 insertions(+), 97 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index 46dc60fe2..c6a448dd3 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -1475,134 +1475,171 @@ RED.editor = (function() { }, { id: "node-config-dialog-ok", - text: adding?RED._("editor.configAdd"):RED._("editor.configUpdate"), + text: adding ? RED._("editor.configAdd") : RED._("editor.configUpdate"), class: "primary", click: function() { - var editState = { + // TODO: Already defined + const configProperty = name; + const configType = type; + const configTypeDef = RED.nodes.getType(configType); + + const wasChanged = editing_config_node.changed; + const editState = { changes: {}, changed: false, outputMap: null }; - var configProperty = name; - var configId = editing_config_node.id; - var configType = type; - var configAdding = adding; - var configTypeDef = RED.nodes.getType(configType); - var d; - var input; + + // Call `oneditsave` and search for changes + handleEditSave(editing_config_node, editState); - if (configTypeDef.oneditsave) { - try { - configTypeDef.oneditsave.call(editing_config_node); - } catch(err) { - console.warn("oneditsave",editing_config_node.id,editing_config_node.type,err.toString()); - } - } - - for (d in configTypeDef.defaults) { - if (configTypeDef.defaults.hasOwnProperty(d)) { - var newValue; - input = $("#node-config-input-"+d); - if (input.attr('type') === "checkbox") { - newValue = input.prop('checked'); - } else if ("format" in configTypeDef.defaults[d] && configTypeDef.defaults[d].format !== "" && input[0].nodeName === "DIV") { - newValue = input.text(); - } else { - newValue = input.val(); - } - if (newValue != null && newValue !== editing_config_node[d]) { - if (editing_config_node._def.defaults[d].type) { - if (newValue == "_ADD_") { - newValue = ""; - } - // Change to a related config node - var configNode = RED.nodes.node(editing_config_node[d]); - if (configNode) { - var users = configNode.users; - users.splice(users.indexOf(editing_config_node),1); - RED.events.emit("nodes:change",configNode); - } - configNode = RED.nodes.node(newValue); - if (configNode) { - configNode.users.push(editing_config_node); - RED.events.emit("nodes:change",configNode); - } - } - editing_config_node[d] = newValue; - } - } - } - - activeEditPanes.forEach(function(pane) { + // Search for changes in the edit box (panes) + activeEditPanes.forEach(function (pane) { if (pane.apply) { pane.apply.call(pane, editState); } - }) + }); - editing_config_node.label = configTypeDef.label; - - var scope = $("#red-ui-editor-config-scope").val(); - editing_config_node.z = scope; + // TODO: Why? + editing_config_node.label = configTypeDef.label + // Check if disabled has changed if ($("#node-config-input-node-disabled").prop('checked')) { if (editing_config_node.d !== true) { + editState.changes.d = editing_config_node.d; + editState.changed = true; editing_config_node.d = true; } } else { if (editing_config_node.d === true) { + editState.changes.d = editing_config_node.d; + editState.changed = true; delete editing_config_node.d; } } + // NOTE: must be undefined if no scope used + const scope = $("#red-ui-editor-config-scope").val() || undefined; + + // Check if the scope has changed + if (editing_config_node.z !== scope) { + editState.changes.z = editing_config_node.z; + editState.changed = true; + editing_config_node.z = scope; + } + + // Search for nodes that use this config node that are no longer + // in scope, so must be removed + const historyEvents = []; if (scope) { - // Search for nodes that use this one that are no longer - // in scope, so must be removed - editing_config_node.users = editing_config_node.users.filter(function(n) { - var keep = true; - for (var d in n._def.defaults) { - if (n._def.defaults.hasOwnProperty(d)) { - if (n._def.defaults[d].type === editing_config_node.type && - n[d] === editing_config_node.id && - n.z !== scope) { - keep = false; - // Remove the reference to this node - // and revalidate - n[d] = null; - n.dirty = true; - n.changed = true; - validateNode(n); + const newUsers = editing_config_node.users.filter(function (node) { + let keepNode = false; + + for (const d in node._def.defaults) { + if (node._def.defaults.hasOwnProperty(d)) { + if (node._def.defaults[d].type === editing_config_node.type) { + if (node[d] === editing_config_node.id) { + if (node.z === editing_config_node.z) { + // The node is kept only if at least one property uses + // this config node in the correct scope. + keepNode = true; + } else { + historyEvents.push({ + t: "edit", + node: node, + changes: { [d]: node[d] }, + changed: node.changed, + dirty: node.dirty + }); + + // Remove the reference to the config + node[d] = ""; + } + } } } } - return keep; - }); - } - if (configAdding) { - RED.nodes.add(editing_config_node); - } - - validateNode(editing_config_node); - var validatedNodes = {}; - validatedNodes[editing_config_node.id] = true; - - var userStack = editing_config_node.users.slice(); - while(userStack.length > 0) { - var user = userStack.pop(); - if (!validatedNodes[user.id]) { - validatedNodes[user.id] = true; - if (user.users) { - userStack = userStack.concat(user.users); + // Mark as changed and revalidate this node + if (!keepNode) { + node.changed = true; + node.dirty = true; + validateNode(node); + RED.events.emit("nodes:change", node); } - validateNode(user); + + return keepNode; + }); + + // Check if users are changed + if (editing_config_node.users.length !== newUsers.length) { + editState.changes.users = editing_config_node.users; + editState.changed = true; + editing_config_node.users = newUsers; } } - RED.nodes.dirty(true); - RED.view.redraw(true); - if (!configAdding) { - RED.events.emit("editor:save",editing_config_node); - RED.events.emit("nodes:change",editing_config_node); + + if (editState.changed) { + // Set the congig node as changed + editing_config_node.changed = true; } + + // Now, validate the config node + validateNode(editing_config_node); + + // And validate nodes using this config node too + const validatedNodes = new Set(); + const userStack = editing_config_node.users.slice(); + + validatedNodes.add(editing_config_node.id); + while (userStack.length) { + const node = userStack.pop(); + if (!validatedNodes.has(node.id)) { + validatedNodes.add(node.id); + if (node.users) { + userStack.push(...node.users); + } + validateNode(node); + } + } + + let historyEvent = { + t: "edit", + node: editing_config_node, + changes: editState.changes, + changed: wasChanged, + dirty: RED.nodes.dirty() + }; + + if (historyEvents.length) { + // Need a multi events + historyEvent = { + t: "multi", + events: [historyEvent].concat(historyEvents), + dirty: historyEvent.dirty + }; + } + + if (!adding) { + // This event is triggered when the edit box is saved, + // regardless of whether there are any modifications. + RED.events.emit("editor:save", editing_config_node); + } + + if (editState.changed) { + if (adding) { + RED.history.push({ t: "add", nodes: [editing_config_node.id], dirty: RED.nodes.dirty() }); + // Add the new config node and trigger the `nodes:add` event + RED.nodes.add(editing_config_node); + } else { + RED.history.push(historyEvent); + RED.events.emit("nodes:change", editing_config_node); + } + + RED.nodes.dirty(true); + RED.view.redraw(true); + } + RED.tray.close(function() { var filter = null; // when editing a config via subflow edit panel, the `configProperty` will not From ed4b98b5982720ea005bc1fcf433d01ccb2bddc6 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Wed, 26 Jun 2024 09:22:01 +0200 Subject: [PATCH 003/121] Fix adding users to history if multiple props modified --- .../editor-client/src/js/ui/editor.js | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index c6a448dd3..1bdd134fb 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -1534,6 +1534,7 @@ RED.editor = (function() { if (scope) { const newUsers = editing_config_node.users.filter(function (node) { let keepNode = false; + let nodeModified = null; for (const d in node._def.defaults) { if (node._def.defaults.hasOwnProperty(d)) { @@ -1544,15 +1545,19 @@ RED.editor = (function() { // this config node in the correct scope. keepNode = true; } else { - historyEvents.push({ - t: "edit", - node: node, - changes: { [d]: node[d] }, - changed: node.changed, - dirty: node.dirty - }); + if (!nodeModified) { + nodeModified = { + t: "edit", + node: node, + changes: { [d]: node[d] }, + changed: node.changed, + dirty: node.dirty + }; + } else { + nodeModified.changes[d] = node[d]; + } - // Remove the reference to the config + // Remove the reference to the config node node[d] = ""; } } @@ -1560,6 +1565,11 @@ RED.editor = (function() { } } + // Add the node modified to the history + if (nodeModified) { + historyEvents.push(nodeModified); + } + // Mark as changed and revalidate this node if (!keepNode) { node.changed = true; From cc1c87387b8cd6792b7e8e53ff10f2500b6a462c Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Wed, 26 Jun 2024 19:10:55 +0200 Subject: [PATCH 004/121] Fix the config node users count --- .../@node-red/editor-client/src/js/nodes.js | 70 +++++++++++++------ .../editor-client/src/js/ui/subflow.js | 6 +- 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 2a7b440f2..14e1598a5 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -706,7 +706,7 @@ RED.nodes = (function() { } else { if (n.wires && (n.wires.length > n.outputs)) { n.outputs = n.wires.length; } n.dirty = true; - updateConfigNodeUsers(n); + updateConfigNodeUsers(newNode, { action: "add" }); if (n._def.category == "subflows" && typeof n.i === "undefined") { var nextId = 0; RED.nodes.eachNode(function(node) { @@ -779,6 +779,7 @@ RED.nodes = (function() { delete nodeLinks[id]; removedLinks = links.filter(function(l) { return (l.source === node) || (l.target === node); }); removedLinks.forEach(removeLink); + updateConfigNodeUsers(node, { action: "remove" }); var updatedConfigNode = false; for (var d in node._def.defaults) { if (node._def.defaults.hasOwnProperty(d)) { @@ -792,10 +793,6 @@ RED.nodes = (function() { if (configNode._def.exclusive) { removeNode(node[d]); removedNodes.push(configNode); - } else { - var users = configNode.users; - users.splice(users.indexOf(node),1); - RED.events.emit('nodes:change',configNode) } } } @@ -1780,9 +1777,20 @@ RED.nodes = (function() { // Replace config nodes // configNodeIds.forEach(function(id) { - removedNodes = removedNodes.concat(convertNode(getNode(id))); + const configNode = getNode(id); + const currentUserCount = configNode.users; + + // Add a snapshot of the Config Node + removedNodes = removedNodes.concat(convertNode(configNode)); + + // Remove the Config Node instance removeNode(id); - importNodes([newConfigNodes[id]]) + + // Import the new one + importNodes([newConfigNodes[id]]); + + // Re-attributes the user count + getNode(id).users = currentUserCount; }); return { @@ -2423,11 +2431,6 @@ RED.nodes = (function() { nodeList = nodeList.map(function(id) { var node = node_map[id]; if (node) { - if (node._def.category === 'config') { - if (node.users.indexOf(n) === -1) { - node.users.push(n); - } - } return node.id; } return id; @@ -2648,19 +2651,44 @@ RED.nodes = (function() { return result; } - // Update any config nodes referenced by the provided node to ensure their 'users' list is correct - function updateConfigNodeUsers(n) { - for (var d in n._def.defaults) { - if (n._def.defaults.hasOwnProperty(d)) { - var property = n._def.defaults[d]; + /** + * Update any config nodes referenced by the provided node to ensure + * their 'users' list is correct. + * + * Options: + * - `action` - Add or remove the node from the Config Node users list. Default `add`. + * - `emitEvent` - Emit the `nodes:changes` event. Default true. + * + * @param {object} node The node in which to check if it contains references + * @param {{ action?: "add" | "remove"; emitEvent?: boolean; }} options Options to apply. + */ + function updateConfigNodeUsers(node, options = {}) { + const defaultOptions = { action: "add", emitEvent: true }; + options = Object.assign({}, defaultOptions, options); + + for (var d in node._def.defaults) { + if (node._def.defaults.hasOwnProperty(d)) { + var property = node._def.defaults[d]; if (property.type) { var type = registry.getNodeType(property.type); if (type && type.category == "config") { - var configNode = configNodes[n[d]]; + var configNode = configNodes[node[d]]; if (configNode) { - if (configNode.users.indexOf(n) === -1) { - configNode.users.push(n); - RED.events.emit('nodes:change',configNode) + if (options.action === "add") { + if (configNode.users.indexOf(node) === -1) { + configNode.users.push(node); + if (options.emitEvent) { + RED.events.emit('nodes:change', configNode); + } + } + } else if (options.action === "remove") { + if (configNode.users.indexOf(node) !== -1) { + const users = configNode.users; + users.splice(users.indexOf(node), 1); + if (options.emitEvent) { + RED.events.emit('nodes:change', configNode); + } + } } } } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js index 751bf9cfb..fe5e81319 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js @@ -508,10 +508,8 @@ RED.subflow = (function() { var activeSubflow = RED.nodes.subflow(id); RED.nodes.eachNode(function(n) { - if (!keepInstanceNodes && n.type == "subflow:"+id) { - removedNodes.push(n); - } - if (n.z == id) { + if (n.z === id || (!keepInstanceNodes && n.type === "subflow:" + id)) { + RED.nodes.updateConfigNodeUsers(n, { action: "remove" }); removedNodes.push(n); } }); From a743764345095ac02300625406af9621cda79754 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Wed, 26 Jun 2024 19:42:26 +0200 Subject: [PATCH 005/121] Fix a node with an invalid number of outputs --- .../@node-red/editor-client/src/js/nodes.js | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 2a7b440f2..f7b61f3a6 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -2321,29 +2321,29 @@ RED.nodes = (function() { node.type = "unknown"; } if (node._def.category != "config") { - if (n.hasOwnProperty('inputs')) { - node.inputs = n.inputs; + if (n.hasOwnProperty('inputs') && def.defaults.hasOwnProperty("inputs")) { + node.inputs = parseInt(n.inputs, 10); node._config.inputs = JSON.stringify(n.inputs); } else { node.inputs = node._def.inputs; } - if (n.hasOwnProperty('outputs')) { - node.outputs = n.outputs; + if (n.hasOwnProperty('outputs') && def.defaults.hasOwnProperty("outputs")) { + node.outputs = parseInt(n.outputs, 10); node._config.outputs = JSON.stringify(n.outputs); } else { node.outputs = node._def.outputs; } - if (node.hasOwnProperty('wires') && node.wires.length > node.outputs) { - if (!node._def.defaults.hasOwnProperty("outputs") || !isNaN(parseInt(n.outputs))) { - // If 'wires' is longer than outputs, clip wires - console.log("Warning: node.wires longer than node.outputs - trimming wires:",node.id," wires:",node.wires.length," outputs:",node.outputs); - node.wires = node.wires.slice(0,node.outputs); - } else { - // The node declares outputs in its defaults, but has not got a valid value - // Defer to the length of the wires array - node.outputs = node.wires.length; - } + + // The node declares outputs in its defaults, but has not got a valid value + // Defer to the length of the wires array + if (isNaN(node.outputs)) { + node.outputs = node.wires.length; + } else if (node.wires.length > node.outputs) { + // If 'wires' is longer than outputs, clip wires + console.log("Warning: node.wires longer than node.outputs - trimming wires:", node.id, " wires:", node.wires.length, " outputs:", node.outputs); + node.wires = node.wires.slice(0, node.outputs); } + for (d in node._def.defaults) { if (node._def.defaults.hasOwnProperty(d) && d !== 'inputs' && d !== 'outputs') { node[d] = n[d]; From 7950ee124150652f58a643dd675007738456a379 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Wed, 26 Jun 2024 21:16:59 +0200 Subject: [PATCH 006/121] Fix updating the subflow name during a copy --- .../@node-red/editor-client/src/js/nodes.js | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 2a7b440f2..2874a024e 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -1032,23 +1032,31 @@ RED.nodes = (function() { return {nodes:removedNodes,links:removedLinks, groups: removedGroups, junctions: removedJunctions}; } + /** + * Add a Subflow to the Workspace + * + * @param {object} sf The Subflow to add. + * @param {boolean|undefined} createNewIds Whether to update the name. + */ function addSubflow(sf, createNewIds) { if (createNewIds) { - var subflowNames = Object.keys(subflows).map(function(sfid) { - return subflows[sfid].name; - }); + // Update the Subflow name to highlight that this is a copy + const subflowNames = Object.keys(subflows).map(function (sfid) { + return subflows[sfid].name || ""; + }).sort(); - subflowNames.sort(); - var copyNumber = 1; - var subflowName = sf.name; + let copyNumber = 1; + let subflowName = sf.name; subflowNames.forEach(function(name) { if (subflowName == name) { + subflowName = sf.name + " (" + copyNumber + ")"; copyNumber++; - subflowName = sf.name+" ("+copyNumber+")"; } }); + sf.name = subflowName; } + subflows[sf.id] = sf; allNodes.addTab(sf.id); linkTabMap[sf.id] = []; @@ -2023,6 +2031,8 @@ RED.nodes = (function() { if (matchingSubflow) { subflow_denylist[n.id] = matchingSubflow; } else { + const oldId = n.id; + subflow_map[n.id] = n; if (createNewIds || options.importMap[n.id] === "copy") { nid = getID(); @@ -2050,7 +2060,7 @@ RED.nodes = (function() { n.status.id = getID(); } new_subflows.push(n); - addSubflow(n,createNewIds || options.importMap[n.id] === "copy"); + addSubflow(n,createNewIds || options.importMap[oldId] === "copy"); } } } From bea08706cc10558012874d45120fc44bb0fe13c6 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Wed, 26 Jun 2024 22:51:08 +0200 Subject: [PATCH 007/121] Handle the import of an incomplete Subflow --- .../@node-red/editor-client/src/js/nodes.js | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 2a7b440f2..6b839714f 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -1049,6 +1049,9 @@ RED.nodes = (function() { }); sf.name = subflowName; } + + sf.instances = []; + subflows[sf.id] = sf; allNodes.addTab(sf.id); linkTabMap[sf.id] = []; @@ -1101,7 +1104,7 @@ RED.nodes = (function() { module: "node-red" } }); - sf.instances = []; + sf._def = RED.nodes.getType("subflow:"+sf.id); RED.events.emit("subflows:add",sf); } @@ -1743,7 +1746,8 @@ RED.nodes = (function() { // Remove the old subflow definition - but leave the instances in place var removalResult = RED.subflow.removeSubflow(n.id, true); // Create the list of nodes for the new subflow def - var subflowNodes = [n].concat(zMap[n.id]); + // Need to sort the list in order to remove missing nodes + var subflowNodes = [n].concat(zMap[n.id]).filter((s) => !!s); // Import the new subflow - no clashes should occur as we've removed // the old version var result = importNodes(subflowNodes); @@ -2170,7 +2174,7 @@ RED.nodes = (function() { x:parseFloat(n.x || 0), y:parseFloat(n.y || 0), z:n.z, - type:0, + type: n.type, info: n.info, changed:false, _config:{} @@ -2261,6 +2265,15 @@ RED.nodes = (function() { outputs: n.outputs|| (n.wires && n.wires.length) || 0, set: registry.getNodeSet("node-red/unknown") } + var orig = {}; + for (var p in n) { + if (n.hasOwnProperty(p) && p!="x" && p!="y" && p!="z" && p!="id" && p!="wires") { + orig[p] = n[p]; + } + } + node._orig = orig; + node.name = n.type; + node.type = "unknown"; } else { if (subflow_denylist[parentId] || createNewIds || options.importMap[n.id] === "copy") { parentId = subflow.id; @@ -2441,9 +2454,11 @@ RED.nodes = (function() { n = new_subflows[i]; n.in.forEach(function(input) { input.wires.forEach(function(wire) { - var link = {source:input, sourcePort:0, target:node_map[wire.id]}; - addLink(link); - new_links.push(link); + if (node_map.hasOwnProperty(wire.id)) { + var link = {source:input, sourcePort:0, target:node_map[wire.id]}; + addLink(link); + new_links.push(link); + } }); delete input.wires; }); @@ -2452,11 +2467,13 @@ RED.nodes = (function() { var link; if (subflow_map[wire.id] && subflow_map[wire.id].id == n.id) { link = {source:n.in[wire.port], sourcePort:wire.port,target:output}; - } else { + } else if (node_map.hasOwnProperty(wire.id) || subflow_map.hasOwnProperty(wire.id)) { link = {source:node_map[wire.id]||subflow_map[wire.id], sourcePort:wire.port,target:output}; } - addLink(link); - new_links.push(link); + if (link) { + addLink(link); + new_links.push(link); + } }); delete output.wires; }); @@ -2465,11 +2482,13 @@ RED.nodes = (function() { var link; if (subflow_map[wire.id] && subflow_map[wire.id].id == n.id) { link = {source:n.in[wire.port], sourcePort:wire.port,target:n.status}; - } else { + } else if (node_map.hasOwnProperty(wire.id) || subflow_map.hasOwnProperty(wire.id)) { link = {source:node_map[wire.id]||subflow_map[wire.id], sourcePort:wire.port,target:n.status}; } - addLink(link); - new_links.push(link); + if (link) { + addLink(link); + new_links.push(link); + } }); delete n.status.wires; } From 10ac7fc369f700e28ba2cf3e6823c2d8069af956 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Sat, 29 Jun 2024 16:08:04 +0200 Subject: [PATCH 008/121] Validate user nodes into history when editing a config node --- .../@node-red/editor-client/src/js/history.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/node_modules/@node-red/editor-client/src/js/history.js b/packages/node_modules/@node-red/editor-client/src/js/history.js index 2fa4e4427..646ec830f 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/history.js +++ b/packages/node_modules/@node-red/editor-client/src/js/history.js @@ -536,6 +536,24 @@ RED.history = (function() { RED.editor.updateNodeProperties(ev.node,outputMap); RED.editor.validateNode(ev.node); } + // If it's a Config Node, validate user nodes too. + // NOTE: The Config Node must be validated before validating users. + if (ev.node.users) { + const validatedNodes = new Set(); + const userStack = ev.node.users.slice(); + + validatedNodes.add(ev.node.id); + while (userStack.length) { + const node = userStack.pop(); + if (!validatedNodes.has(node.id)) { + validatedNodes.add(node.id); + if (node.users) { + userStack.push(...node.users); + } + RED.editor.validateNode(node); + } + } + } if (ev.links) { inverseEv.createdLinks = []; for (i=0;i Date: Mon, 1 Jul 2024 17:43:39 +0200 Subject: [PATCH 009/121] Update packages/node_modules/@node-red/editor-client/src/js/nodes.js Co-authored-by: Nick O'Leary --- packages/node_modules/@node-red/editor-client/src/js/nodes.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 2874a024e..b940bfc32 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -1043,7 +1043,8 @@ RED.nodes = (function() { // Update the Subflow name to highlight that this is a copy const subflowNames = Object.keys(subflows).map(function (sfid) { return subflows[sfid].name || ""; - }).sort(); + }) + subflowNames.sort() let copyNumber = 1; let subflowName = sf.name; From 8aec038c2453411fb730debc4a8c9c8adade59a3 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Mon, 1 Jul 2024 17:59:28 +0200 Subject: [PATCH 010/121] Remove duplicate type definition --- packages/node_modules/@node-red/editor-client/src/js/nodes.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 6b839714f..c24da629e 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -2235,7 +2235,6 @@ RED.nodes = (function() { } } } - node.type = n.type; node._def = def; if (node.type === "group") { node._def = RED.group.def; From af42664d7355f638de80df581da56c37bb06df03 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Sat, 20 Jul 2024 13:36:37 +0900 Subject: [PATCH 011/121] Refresh page title after changing tab name --- .../@node-red/editor-client/src/js/ui/workspaces.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js index 0d7b1da54..365d827f7 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js @@ -904,6 +904,13 @@ RED.workspaces = (function() { } }, refresh: function() { + var workspace = RED.nodes.workspace(RED.workspaces.active()); + if (workspace) { + document.title = `${documentTitle} : ${workspace.label}`; + } else { + var subflow = RED.nodes.subflow(RED.workspaces.active()); + document.title = `${documentTitle} : ${subflow.name}`; + } RED.nodes.eachWorkspace(function(ws) { workspace_tabs.renameTab(ws.id,ws.label); $("#red-ui-tab-"+(ws.id.replace(".","-"))).attr("flowname",ws.label) From b5785dab9c9fe635f9de80972790642c23f9a232 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Tue, 13 Aug 2024 23:48:28 +0900 Subject: [PATCH 012/121] Add Japanese translations for v4.0.2 (again) --- .../@node-red/editor-client/locales/en-US/editor.json | 4 +++- .../@node-red/editor-client/locales/ja/editor.json | 4 +++- .../node_modules/@node-red/editor-client/src/js/ui/library.js | 4 ++-- .../@node-red/editor-client/src/js/ui/tab-info.js | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index 3f6617752..9bdd55155 100644 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -562,7 +562,9 @@ "types": { "local": "Local", "examples": "Examples" - } + }, + "type": "Type", + "name": "Name" }, "palette": { "noInfo": "no information available", diff --git a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json index 5b11e24c4..c0296f457 100644 --- a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json @@ -562,7 +562,9 @@ "types": { "local": "ローカル", "examples": "サンプル" - } + }, + "type": "型", + "name": "名前" }, "palette": { "noInfo": "情報がありません", diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/library.js b/packages/node_modules/@node-red/editor-client/src/js/ui/library.js index d276b1572..0098bc947 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/library.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/library.js @@ -839,10 +839,10 @@ RED.library = (function() { if (file && file.label && !file.children) { $.get("library/"+file.library+"/"+file.type+"/"+file.path, function(data) { //TODO: nls + sanitize - var propRow = $('Type').appendTo(table); + var propRow = $(''+RED._("library.type")+'').appendTo(table); $(propRow.children()[1]).text(activeLibrary.type); if (file.props.hasOwnProperty('name')) { - propRow = $('Name'+file.props.name+'').appendTo(table); + propRow = $(''+RED._("library.name")+''+file.props.name+'').appendTo(table); $(propRow.children()[1]).text(file.props.name); } for (var p in file.props) { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js index f72a7b3f2..fa9b98322 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js @@ -204,7 +204,7 @@ RED.sidebar.info = (function() { propertiesPanelHeaderIcon.empty(); RED.utils.createNodeIcon({type:"_selection_"}).appendTo(propertiesPanelHeaderIcon); - propertiesPanelHeaderLabel.text("Selection"); + propertiesPanelHeaderLabel.text(RED._("sidebar.info.selection")); propertiesPanelHeaderReveal.hide(); propertiesPanelHeaderHelp.hide(); propertiesPanelHeaderCopyLink.hide(); From b4d29d4d4a2eb16f6daa9fd2abee7e973909cfba Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Wed, 14 Aug 2024 00:25:59 +0900 Subject: [PATCH 013/121] Fix typo in flow example name --- ... Join extracted sequence of HTML element using join node.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/node_modules/@node-red/nodes/examples/parser/html/{04 - Join extracedt sequence of HTML element using join node.json => 04 - Join extracted sequence of HTML element using join node.json} (100%) diff --git a/packages/node_modules/@node-red/nodes/examples/parser/html/04 - Join extracedt sequence of HTML element using join node.json b/packages/node_modules/@node-red/nodes/examples/parser/html/04 - Join extracted sequence of HTML element using join node.json similarity index 100% rename from packages/node_modules/@node-red/nodes/examples/parser/html/04 - Join extracedt sequence of HTML element using join node.json rename to packages/node_modules/@node-red/nodes/examples/parser/html/04 - Join extracted sequence of HTML element using join node.json From 27604fef232eeb4df73abd59c7b46d3ef56ded92 Mon Sep 17 00:00:00 2001 From: Luis Antonio Obis Aparicio Date: Sat, 31 Aug 2024 04:32:01 +0200 Subject: [PATCH 014/121] add citation file --- CITATION.cff | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 CITATION.cff diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 000000000..9372ad005 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,7 @@ +cff-version: 1.2.0 +message: "If you use this software, please cite it as below." +title: "Node-RED" +authors: + - family-names: "OpenJS Foundation" + - family-names: "Contributors" +url: "https://nodered.org" From 20ae4a7272f183a1b19593a01756ee7355b2b9b7 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 11 Sep 2024 13:58:16 +0100 Subject: [PATCH 015/121] Update dependencies --- package.json | 12 ++++++------ .../node_modules/@node-red/editor-api/package.json | 4 ++-- packages/node_modules/@node-red/nodes/package.json | 8 ++++---- packages/node_modules/@node-red/runtime/package.json | 2 +- packages/node_modules/node-red/package.json | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index f705e0752..06161a639 100644 --- a/package.json +++ b/package.json @@ -26,13 +26,13 @@ } ], "dependencies": { - "acorn": "8.11.3", - "acorn-walk": "8.3.2", - "ajv": "8.14.0", + "acorn": "8.12.1", + "acorn-walk": "8.3.4", + "ajv": "8.17.1", "async-mutex": "0.5.0", "basic-auth": "2.0.1", "bcryptjs": "2.4.3", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "cheerio": "1.0.0-rc.10", "clone": "2.1.2", "content-type": "1.0.5", @@ -41,7 +41,7 @@ "cors": "2.8.5", "cronosjs": "1.7.1", "denque": "2.1.0", - "express": "4.19.2", + "express": "4.20.0", "express-session": "1.18.0", "form-data": "4.0.0", "fs-extra": "11.2.0", @@ -64,7 +64,7 @@ "mqtt": "5.7.0", "multer": "1.4.5-lts.1", "mustache": "4.2.0", - "node-red-admin": "^4.0.0", + "node-red-admin": "^4.0.1", "node-watch": "0.7.4", "nopt": "5.0.0", "oauth2orize": "1.12.0", diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json index 792b5b67f..f46a31a02 100644 --- a/packages/node_modules/@node-red/editor-api/package.json +++ b/packages/node_modules/@node-red/editor-api/package.json @@ -19,11 +19,11 @@ "@node-red/util": "4.0.2", "@node-red/editor-client": "4.0.2", "bcryptjs": "2.4.3", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "clone": "2.1.2", "cors": "2.8.5", "express-session": "1.18.0", - "express": "4.19.2", + "express": "4.20.0", "memorystore": "1.6.7", "mime": "3.0.0", "multer": "1.4.5-lts.1", diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json index e8e384948..45f0de327 100644 --- a/packages/node_modules/@node-red/nodes/package.json +++ b/packages/node_modules/@node-red/nodes/package.json @@ -15,10 +15,10 @@ } ], "dependencies": { - "acorn": "8.11.3", - "acorn-walk": "8.3.2", - "ajv": "8.14.0", - "body-parser": "1.20.2", + "acorn": "8.12.1", + "acorn-walk": "8.3.4", + "ajv": "8.17.1", + "body-parser": "1.20.3", "cheerio": "1.0.0-rc.10", "content-type": "1.0.5", "cookie-parser": "1.4.6", diff --git a/packages/node_modules/@node-red/runtime/package.json b/packages/node_modules/@node-red/runtime/package.json index d6a7a0009..a6ae8d8dc 100644 --- a/packages/node_modules/@node-red/runtime/package.json +++ b/packages/node_modules/@node-red/runtime/package.json @@ -20,7 +20,7 @@ "@node-red/util": "4.0.2", "async-mutex": "0.5.0", "clone": "2.1.2", - "express": "4.19.2", + "express": "4.20.0", "fs-extra": "11.2.0", "json-stringify-safe": "5.0.1", "rfdc": "^1.3.1" diff --git a/packages/node_modules/node-red/package.json b/packages/node_modules/node-red/package.json index 93c90f807..a7de8cd80 100644 --- a/packages/node_modules/node-red/package.json +++ b/packages/node_modules/node-red/package.json @@ -38,9 +38,9 @@ "basic-auth": "2.0.1", "bcryptjs": "2.4.3", "cors": "2.8.5", - "express": "4.19.2", + "express": "4.20.0", "fs-extra": "11.2.0", - "node-red-admin": "^4.0.0", + "node-red-admin": "^4.0.1", "nopt": "5.0.0", "semver": "7.5.4" }, From 92911f6714d0d1f8063368fbc6fc117b3a1f3856 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 11 Sep 2024 17:37:50 +0100 Subject: [PATCH 016/121] Update tough-cookie --- package.json | 2 +- packages/node_modules/@node-red/nodes/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 06161a639..b0b5b1e8b 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "rfdc": "^1.3.1", "semver": "7.5.4", "tar": "7.2.0", - "tough-cookie": "4.1.4", + "tough-cookie": "^5.0.0", "uglify-js": "3.17.4", "uuid": "9.0.1", "ws": "7.5.10", diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json index 45f0de327..b5cbc16b9 100644 --- a/packages/node_modules/@node-red/nodes/package.json +++ b/packages/node_modules/@node-red/nodes/package.json @@ -41,7 +41,7 @@ "node-watch": "0.7.4", "on-headers": "1.0.2", "raw-body": "2.5.2", - "tough-cookie": "4.1.4", + "tough-cookie": "^5.0.0", "uuid": "9.0.1", "ws": "7.5.10", "xml2js": "0.6.2", From d76b0d39cd18cea065c93ed3637b47bbdbda9e04 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 11 Sep 2024 17:47:26 +0100 Subject: [PATCH 017/121] Remove use of util.log --- .../node_modules/@node-red/util/lib/log.js | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/node_modules/@node-red/util/lib/log.js b/packages/node_modules/@node-red/util/lib/log.js index 341019080..7b7e9b2dc 100644 --- a/packages/node_modules/@node-red/util/lib/log.js +++ b/packages/node_modules/@node-red/util/lib/log.js @@ -75,12 +75,28 @@ LogHandler.prototype.shouldReportMessage = function(msglevel) { msglevel <= this.logLevel; } + +// Older versions of Node-RED used the deprecated util.log function. +// With Node.js 22, use of that function causes warnings. So here we +// are replicating the same format output to ensure we don't break any +// log parsing that happens in the real world. +const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; +const utilLog = function (msg) { + const d = new Date(); + const time = [ + d.getHours().toString().padStart(2, '0'), + d.getMinutes().toString().padStart(2, '0'), + d.getSeconds().toString().padStart(2, '0') + ].join(':'); + console.log(`${d.getDate()} ${months[d.getMonth()]} ${time} - ${msg}`) +} + var consoleLogger = function(msg) { if (msg.level == log.METRIC || msg.level == log.AUDIT) { - util.log("["+levelNames[msg.level]+"] "+JSON.stringify(msg)); + utilLog("["+levelNames[msg.level]+"] "+JSON.stringify(msg)); } else { if (verbose && msg.msg && msg.msg.stack) { - util.log("["+levelNames[msg.level]+"] "+(msg.type?"["+msg.type+":"+(msg.name||msg.id)+"] ":"")+msg.msg.stack); + utilLog("["+levelNames[msg.level]+"] "+(msg.type?"["+msg.type+":"+(msg.name||msg.id)+"] ":"")+msg.msg.stack); } else { var message = msg.msg; try { @@ -91,7 +107,7 @@ var consoleLogger = function(msg) { message = 'Exception trying to log: '+util.inspect(message); } - util.log("["+levelNames[msg.level]+"] "+(msg.type?"["+msg.type+":"+(msg.name||msg.id)+"] ":"")+message); + utilLog("["+levelNames[msg.level]+"] "+(msg.type?"["+msg.type+":"+(msg.name||msg.id)+"] ":"")+message); } } } From d4b001a74e4c88c9d3edc8d0a7b2cdda23bb0830 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 11 Sep 2024 18:28:35 +0100 Subject: [PATCH 018/121] Fix tests for util.log removal --- test/unit/@node-red/util/lib/log_spec.js | 79 ++++++++++++------------ 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/test/unit/@node-red/util/lib/log_spec.js b/test/unit/@node-red/util/lib/log_spec.js index 056f37672..8fe09b3bf 100644 --- a/test/unit/@node-red/util/lib/log_spec.js +++ b/test/unit/@node-red/util/lib/log_spec.js @@ -24,38 +24,38 @@ var log = NR_TEST_UTILS.require("@node-red/util").log; describe("@node-red/util/log", function() { beforeEach(function () { - var spy = sinon.stub(util, 'log').callsFake(function(arg){}); + var spy = sinon.stub(console, 'log').callsFake(function(arg){}); var settings = {logging: { console: { level: 'metric', metrics: true } } }; log.init(settings); }); afterEach(function() { - util.log.restore(); + console.log.restore(); }); it('it can raise an error', function() { var ret = log.error("This is an error"); - sinon.assert.calledWithMatch(util.log,"[error] This is an error"); + sinon.assert.calledWithMatch(console.log,"[error] This is an error"); }); it('it can raise a trace', function() { var ret = log.trace("This is a trace"); - sinon.assert.calledWithMatch(util.log,"[trace] This is a trace"); + sinon.assert.calledWithMatch(console.log,"[trace] This is a trace"); }); it('it can raise a debug', function() { var ret = log.debug("This is a debug"); - sinon.assert.calledWithMatch(util.log,"[debug] This is a debug"); + sinon.assert.calledWithMatch(console.log,"[debug] This is a debug"); }); it('it can raise a info', function() { var ret = log.info("This is an info"); - sinon.assert.calledWithMatch(util.log,"[info] This is an info"); + sinon.assert.calledWithMatch(console.log,"[info] This is an info"); }); it('it can raise a warn', function() { var ret = log.warn("This is a warn"); - sinon.assert.calledWithMatch(util.log,"[warn] This is a warn"); + sinon.assert.calledWithMatch(console.log,"[warn] This is a warn"); }); it('it can raise a metric', function() { @@ -66,9 +66,10 @@ describe("@node-red/util/log", function() { metrics.msgid = "12345"; metrics.value = "the metric payload"; var ret = log.log(metrics); - util.log.calledOnce.should.be.true(); - util.log.firstCall.args[0].indexOf("[metric] ").should.equal(0); - var body = JSON.parse(util.log.firstCall.args[0].substring(9)); + console.log.calledOnce.should.be.true(); + console.log.firstCall.args[0].indexOf("[metric]").should.not.equal(-1); + const parts = console.log.firstCall.args[0].split("[metric] ") + var body = JSON.parse(parts[1]) body.should.have.a.property("nodeid","testid"); body.should.have.a.property("event","node.test.testevent"); body.should.have.a.property("msgid","12345"); @@ -86,13 +87,13 @@ describe("@node-red/util/log", function() { it('it logs node type and name if provided',function() { log.log({level:log.INFO,type:"nodeType",msg:"test",name:"nodeName",id:"nodeId"}); - util.log.calledOnce.should.be.true(); - util.log.firstCall.args[0].indexOf("[nodeType:nodeName]").should.not.equal(-1); + console.log.calledOnce.should.be.true(); + console.log.firstCall.args[0].indexOf("[nodeType:nodeName]").should.not.equal(-1); }); it('it logs node type and id if no name provided',function() { log.log({level:log.INFO,type:"nodeType",msg:"test",id:"nodeId"}); - util.log.calledOnce.should.be.true(); - util.log.firstCall.args[0].indexOf("[nodeType:nodeId]").should.not.equal(-1); + console.log.calledOnce.should.be.true(); + console.log.firstCall.args[0].indexOf("[nodeType:nodeId]").should.not.equal(-1); }); it('ignores lower level messages and metrics', function() { @@ -104,12 +105,12 @@ describe("@node-red/util/log", function() { log.debug("This is a debug"); log.trace("This is a trace"); log.log({level:log.METRIC,msg:"testMetric"}); - sinon.assert.calledWithMatch(util.log,"[error] This is an error"); - sinon.assert.calledWithMatch(util.log,"[warn] This is a warn"); - sinon.assert.neverCalledWithMatch(util.log,"[info] This is an info"); - sinon.assert.neverCalledWithMatch(util.log,"[debug] This is a debug"); - sinon.assert.neverCalledWithMatch(util.log,"[trace] This is a trace"); - sinon.assert.neverCalledWithMatch(util.log,"[metric] "); + sinon.assert.calledWithMatch(console.log,"[error] This is an error"); + sinon.assert.calledWithMatch(console.log,"[warn] This is a warn"); + sinon.assert.neverCalledWithMatch(console.log,"[info] This is an info"); + sinon.assert.neverCalledWithMatch(console.log,"[debug] This is a debug"); + sinon.assert.neverCalledWithMatch(console.log,"[trace] This is a trace"); + sinon.assert.neverCalledWithMatch(console.log,"[metric] "); }); it('ignores lower level messages but accepts metrics', function() { var settings = {logging: { console: { level: 'log', metrics: true } } }; @@ -120,12 +121,12 @@ describe("@node-red/util/log", function() { log.debug("This is a debug"); log.trace("This is a trace"); log.log({level:log.METRIC,msg:"testMetric"}); - sinon.assert.calledWithMatch(util.log,"[error] This is an error"); - sinon.assert.calledWithMatch(util.log,"[warn] This is a warn"); - sinon.assert.calledWithMatch(util.log,"[info] This is an info"); - sinon.assert.neverCalledWithMatch(util.log,"[debug] This is a debug"); - sinon.assert.neverCalledWithMatch(util.log,"[trace] This is a trace"); - sinon.assert.calledWithMatch(util.log,"[metric] "); + sinon.assert.calledWithMatch(console.log,"[error] This is an error"); + sinon.assert.calledWithMatch(console.log,"[warn] This is a warn"); + sinon.assert.calledWithMatch(console.log,"[info] This is an info"); + sinon.assert.neverCalledWithMatch(console.log,"[debug] This is a debug"); + sinon.assert.neverCalledWithMatch(console.log,"[trace] This is a trace"); + sinon.assert.calledWithMatch(console.log,"[metric] "); }); it('default settings set to INFO and metrics off', function() { @@ -136,12 +137,12 @@ describe("@node-red/util/log", function() { log.debug("This is a debug"); log.trace("This is a trace"); log.log({level:log.METRIC,msg:"testMetric"}); - sinon.assert.calledWithMatch(util.log,"[error] This is an error"); - sinon.assert.calledWithMatch(util.log,"[warn] This is a warn"); - sinon.assert.calledWithMatch(util.log,"[info] This is an info"); - sinon.assert.neverCalledWithMatch(util.log,"[debug] This is a debug"); - sinon.assert.neverCalledWithMatch(util.log,"[trace] This is a trace"); - sinon.assert.neverCalledWithMatch(util.log,"[metric] "); + sinon.assert.calledWithMatch(console.log,"[error] This is an error"); + sinon.assert.calledWithMatch(console.log,"[warn] This is a warn"); + sinon.assert.calledWithMatch(console.log,"[info] This is an info"); + sinon.assert.neverCalledWithMatch(console.log,"[debug] This is a debug"); + sinon.assert.neverCalledWithMatch(console.log,"[trace] This is a trace"); + sinon.assert.neverCalledWithMatch(console.log,"[metric] "); }); it('no logger used if custom logger handler does not exist', function() { var settings = {logging: { customLogger: { level: 'trace', metrics: true } } }; @@ -152,12 +153,12 @@ describe("@node-red/util/log", function() { log.debug("This is a debug"); log.trace("This is a trace"); log.log({level:log.METRIC,msg:"testMetric"}); - sinon.assert.neverCalledWithMatch(util.log,"[error] This is an error"); - sinon.assert.neverCalledWithMatch(util.log,"[warn] This is a warn"); - sinon.assert.neverCalledWithMatch(util.log,"[info] This is an info"); - sinon.assert.neverCalledWithMatch(util.log,"[debug] This is a debug"); - sinon.assert.neverCalledWithMatch(util.log,"[trace] This is a trace"); - sinon.assert.neverCalledWithMatch(util.log,"[metric] "); + sinon.assert.neverCalledWithMatch(console.log,"[error] This is an error"); + sinon.assert.neverCalledWithMatch(console.log,"[warn] This is a warn"); + sinon.assert.neverCalledWithMatch(console.log,"[info] This is an info"); + sinon.assert.neverCalledWithMatch(console.log,"[debug] This is a debug"); + sinon.assert.neverCalledWithMatch(console.log,"[trace] This is a trace"); + sinon.assert.neverCalledWithMatch(console.log,"[metric] "); }); it('add a custom log handler directly', function() { @@ -244,7 +245,7 @@ describe("@node-red/util/log", function() { }, }; var ret = log.info(msg.msg); - sinon.assert.calledWithMatch(util.log,"my special message"); + sinon.assert.calledWithMatch(console.log,"my special message"); }); From 89839f433bb69b2b436f07b7c277d5925eef2bd1 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 13 Sep 2024 14:35:46 +0100 Subject: [PATCH 019/121] Do not include Junction type in quick-add for virtual links --- .../editor-client/src/js/ui/typeSearch.js | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js b/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js index 703b57309..216cc3e40 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js @@ -335,13 +335,25 @@ RED.typeSearch = (function() { } } function applyFilter(filter,type,def) { - return !def || !filter || - ( - (!filter.spliceMultiple) && - (!filter.type || type === filter.type) && - (!filter.input || type === 'junction' || def.inputs > 0) && - (!filter.output || type === 'junction' || def.outputs > 0) - ) + if (!filter) { + // No filter; allow everything + return true + } + if (type === 'junction') { + // Only allow Junction is there's no specific type filter + return !filter.type + } + if (filter.type) { + // Handle explicit type filter + return filter.type === type + } + if (!def) { + // No node definition available - allow it + return true + } + // Check if the filter is for input/outputs and apply + return (!filter.input || def.inputs > 0) && + (!filter.output || def.outputs > 0) } function refreshTypeList(opts) { var i; From 64cdee6d36a17ecf274f72eedbb9703a8a374df2 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 13 Sep 2024 14:57:09 +0100 Subject: [PATCH 020/121] Update express --- package.json | 2 +- packages/node_modules/@node-red/editor-api/package.json | 2 +- packages/node_modules/@node-red/runtime/package.json | 2 +- packages/node_modules/node-red/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index b0b5b1e8b..ae163fe62 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "cors": "2.8.5", "cronosjs": "1.7.1", "denque": "2.1.0", - "express": "4.20.0", + "express": "4.21.0", "express-session": "1.18.0", "form-data": "4.0.0", "fs-extra": "11.2.0", diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json index f46a31a02..e8b09f443 100644 --- a/packages/node_modules/@node-red/editor-api/package.json +++ b/packages/node_modules/@node-red/editor-api/package.json @@ -23,7 +23,7 @@ "clone": "2.1.2", "cors": "2.8.5", "express-session": "1.18.0", - "express": "4.20.0", + "express": "4.21.0", "memorystore": "1.6.7", "mime": "3.0.0", "multer": "1.4.5-lts.1", diff --git a/packages/node_modules/@node-red/runtime/package.json b/packages/node_modules/@node-red/runtime/package.json index a6ae8d8dc..2203b5f8e 100644 --- a/packages/node_modules/@node-red/runtime/package.json +++ b/packages/node_modules/@node-red/runtime/package.json @@ -20,7 +20,7 @@ "@node-red/util": "4.0.2", "async-mutex": "0.5.0", "clone": "2.1.2", - "express": "4.20.0", + "express": "4.21.0", "fs-extra": "11.2.0", "json-stringify-safe": "5.0.1", "rfdc": "^1.3.1" diff --git a/packages/node_modules/node-red/package.json b/packages/node_modules/node-red/package.json index a7de8cd80..3ea55b756 100644 --- a/packages/node_modules/node-red/package.json +++ b/packages/node_modules/node-red/package.json @@ -38,7 +38,7 @@ "basic-auth": "2.0.1", "bcryptjs": "2.4.3", "cors": "2.8.5", - "express": "4.20.0", + "express": "4.21.0", "fs-extra": "11.2.0", "node-red-admin": "^4.0.1", "nopt": "5.0.0", From f44868384e7fb12d30f0ac1b5788074b6321b7fc Mon Sep 17 00:00:00 2001 From: Ben Hardill Date: Mon, 16 Sep 2024 11:55:05 +0100 Subject: [PATCH 021/121] Move SNI, ALPN and Verify Server cert out of check This moves the SNI, ALPN and the Verify Server Cert check out of the check for if the supplied certs/key are actually valid as these may be still required for correct behaviour. part of #4877 --- .../@node-red/nodes/core/network/05-tls.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/network/05-tls.js b/packages/node_modules/@node-red/nodes/core/network/05-tls.js index 888d749fd..f47067729 100644 --- a/packages/node_modules/@node-red/nodes/core/network/05-tls.js +++ b/packages/node_modules/@node-red/nodes/core/network/05-tls.js @@ -104,14 +104,14 @@ module.exports = function(RED) { if (this.credentials && this.credentials.passphrase) { opts.passphrase = this.credentials.passphrase; } - if (this.servername) { - opts.servername = this.servername; - } - if (this.alpnprotocol) { - opts.ALPNProtocols = [this.alpnprotocol]; - } - opts.rejectUnauthorized = this.verifyservercert; } + if (this.servername) { + opts.servername = this.servername; + } + if (this.alpnprotocol) { + opts.ALPNProtocols = [this.alpnprotocol]; + } + opts.rejectUnauthorized = this.verifyservercert; return opts; } From aa74d8160ac900cbac9935f1b96a96e51f69bd85 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 16 Sep 2024 16:39:41 +0100 Subject: [PATCH 022/121] Stay in quick-add mode following context menu insert Fixes #4881 --- .../@node-red/editor-client/src/js/ui/view.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index fd1205a30..40ec0d5bf 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -1209,7 +1209,11 @@ RED.view = (function() { lasso = null; } if (d3.event.touches || d3.event.button === 0) { - if ((mouse_mode === 0 || mouse_mode === RED.state.QUICK_JOINING) && isControlPressed(d3.event) && !(d3.event.altKey || d3.event.shiftKey)) { + if ( + (mouse_mode === 0 && isControlPressed(d3.event) && !(d3.event.altKey || d3.event.shiftKey)) + || + mouse_mode === RED.state.QUICK_JOINING + ) { // Trigger quick add dialog d3.event.stopPropagation(); clearSelection(); @@ -1285,7 +1289,6 @@ RED.view = (function() { } var mainPos = $("#red-ui-main-container").position(); - if (mouse_mode !== RED.state.QUICK_JOINING) { mouse_mode = RED.state.QUICK_JOINING; $(window).on('keyup',disableQuickJoinEventHandler); @@ -3057,8 +3060,8 @@ RED.view = (function() { } function disableQuickJoinEventHandler(evt) { - // Check for ctrl (all browsers), "Meta" (Chrome/FF), keyCode 91 (Safari) - if (evt.keyCode === 17 || evt.key === "Meta" || evt.keyCode === 91) { + // Check for ctrl (all browsers), "Meta" (Chrome/FF), keyCode 91 (Safari), or Escape + if (evt.keyCode === 17 || evt.key === "Meta" || evt.keyCode === 91 || evt.keyCode === 27) { resetMouseVars(); hideDragLines(); redraw(); From 83b30f1c18bcf16f0bec2d64458af77de942308e Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 16 Sep 2024 16:45:22 +0100 Subject: [PATCH 023/121] Fix linting --- .../node_modules/@node-red/editor-client/src/js/ui/view.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index 40ec0d5bf..ff0091e65 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -1210,10 +1210,9 @@ RED.view = (function() { } if (d3.event.touches || d3.event.button === 0) { if ( - (mouse_mode === 0 && isControlPressed(d3.event) && !(d3.event.altKey || d3.event.shiftKey)) - || - mouse_mode === RED.state.QUICK_JOINING - ) { + (mouse_mode === 0 && isControlPressed(d3.event) && !(d3.event.altKey || d3.event.shiftKey)) || + mouse_mode === RED.state.QUICK_JOINING + ) { // Trigger quick add dialog d3.event.stopPropagation(); clearSelection(); From 5a3e6925e54dfd47436a5c1f4bd72889730f0aab Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 17 Sep 2024 14:15:42 +0100 Subject: [PATCH 024/121] Update packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js --- .../@node-red/editor-client/src/js/ui/workspaces.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js index 365d827f7..bd52498f1 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js @@ -909,7 +909,11 @@ RED.workspaces = (function() { document.title = `${documentTitle} : ${workspace.label}`; } else { var subflow = RED.nodes.subflow(RED.workspaces.active()); - document.title = `${documentTitle} : ${subflow.name}`; + if (subflow) { + document.title = `${documentTitle} : ${subflow.name}`; + } else { + document.title = documentTitle + } } RED.nodes.eachWorkspace(function(ws) { workspace_tabs.renameTab(ws.id,ws.label); From f7a43f83e5c3339c9edd51c354bab280971d6699 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 17 Sep 2024 14:23:43 +0100 Subject: [PATCH 025/121] Bump for 4.0.3 --- .github/workflows/tests.yml | 2 +- CHANGELOG.md | 35 +++++++++++++++++++ package.json | 2 +- .../@node-red/editor-api/package.json | 6 ++-- .../@node-red/editor-client/package.json | 2 +- .../node_modules/@node-red/nodes/package.json | 2 +- .../@node-red/registry/package.json | 4 +-- .../@node-red/runtime/package.json | 6 ++-- .../node_modules/@node-red/util/package.json | 2 +- packages/node_modules/node-red/package.json | 10 +++--- 10 files changed, 53 insertions(+), 18 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cf871716a..70d36deb1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [18, 20, 22.4.x] + node-version: [18, 20, 22] steps: - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} diff --git a/CHANGELOG.md b/CHANGELOG.md index e4ebd3087..6eaa3b7fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,38 @@ +#### 4.0.3: Maintenance Release + +Editor + + - Refresh page title after changing tab name (#4850) @kazuhitoyokoi + - Add Japanese translations for v4.0.2 (again) (#4853) @kazuhitoyokoi + - Stay in quick-add mode following context menu insert (#4883) @knolleary + - Do not include Junction type in quick-add for virtual links (#4879) @knolleary + - Multiplayer cursor tracking (#4845) @knolleary + - Hide add-flow options when disabled via editorTheme (#4869) @knolleary + - Fix env-var config select when multiple defined (#4872) @knolleary + - Fix subflow outbound-link filter (#4857) @GogoVega + - Add French translations for v4.0.2 (#4856) @GogoVega + - Fix moving link wires (#4851) @knolleary + - Adjust type search dialog position to prevent x-overflow (#4844) @Steve-Mcl + - fix: modulesInUse might be undefined (#4838) @lorenz-maurer + - Add Japanese translations for v4.0.2 (#4849) @kazuhitoyokoi + - Fix menu to enable/disable selection when it's a group (#4828) @GogoVega + +Runtime + + - Update dependencies (#4874) @knolleary + - GitHub: Add citation file to enable "Cite this repository" feature (#4861) @lobis + - Remove use of util.log (#4875) @knolleary + +Nodes + + - Fix invalid property error in range node example (#4855) + - Fix typo in flow example name (#4854) @kazuhitoyokoi + - Move SNI, ALPN and Verify Server cert out of check (#4882) @hardillb + - Set status of mqtt nodes to "disconnected" when deregistered from broker (#4878) @Steve-Mcl + - MQTT: Ensure will payload is a string (#4873) @knolleary + - Let batch node terminate "early" if msg.parts set to end of sequence (#4829) @dceejay + - Fix unintentional Capitalisation in Split node name (#4835) @dceejay + #### 4.0.2: Maintenance Release Editor diff --git a/package.json b/package.json index ae163fe62..5c41a2999 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "4.0.2", + "version": "4.0.3", "description": "Low-code programming for event-driven applications", "homepage": "https://nodered.org", "license": "Apache-2.0", diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json index e8b09f443..59a265ca4 100644 --- a/packages/node_modules/@node-red/editor-api/package.json +++ b/packages/node_modules/@node-red/editor-api/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-api", - "version": "4.0.2", + "version": "4.0.3", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/util": "4.0.2", - "@node-red/editor-client": "4.0.2", + "@node-red/util": "4.0.3", + "@node-red/editor-client": "4.0.3", "bcryptjs": "2.4.3", "body-parser": "1.20.3", "clone": "2.1.2", diff --git a/packages/node_modules/@node-red/editor-client/package.json b/packages/node_modules/@node-red/editor-client/package.json index 932afb7ac..7e65dcc1a 100644 --- a/packages/node_modules/@node-red/editor-client/package.json +++ b/packages/node_modules/@node-red/editor-client/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-client", - "version": "4.0.2", + "version": "4.0.3", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json index b5cbc16b9..dcbb59b24 100644 --- a/packages/node_modules/@node-red/nodes/package.json +++ b/packages/node_modules/@node-red/nodes/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/nodes", - "version": "4.0.2", + "version": "4.0.3", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/registry/package.json b/packages/node_modules/@node-red/registry/package.json index 1d54f76d8..2515b19b7 100644 --- a/packages/node_modules/@node-red/registry/package.json +++ b/packages/node_modules/@node-red/registry/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/registry", - "version": "4.0.2", + "version": "4.0.3", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,7 +16,7 @@ } ], "dependencies": { - "@node-red/util": "4.0.2", + "@node-red/util": "4.0.3", "clone": "2.1.2", "fs-extra": "11.2.0", "semver": "7.5.4", diff --git a/packages/node_modules/@node-red/runtime/package.json b/packages/node_modules/@node-red/runtime/package.json index 2203b5f8e..67464f496 100644 --- a/packages/node_modules/@node-red/runtime/package.json +++ b/packages/node_modules/@node-red/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/runtime", - "version": "4.0.2", + "version": "4.0.3", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/registry": "4.0.2", - "@node-red/util": "4.0.2", + "@node-red/registry": "4.0.3", + "@node-red/util": "4.0.3", "async-mutex": "0.5.0", "clone": "2.1.2", "express": "4.21.0", diff --git a/packages/node_modules/@node-red/util/package.json b/packages/node_modules/@node-red/util/package.json index 1f25923bb..d747109dd 100644 --- a/packages/node_modules/@node-red/util/package.json +++ b/packages/node_modules/@node-red/util/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/util", - "version": "4.0.2", + "version": "4.0.3", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/node-red/package.json b/packages/node_modules/node-red/package.json index 3ea55b756..b6c767e9b 100644 --- a/packages/node_modules/node-red/package.json +++ b/packages/node_modules/node-red/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "4.0.2", + "version": "4.0.3", "description": "Low-code programming for event-driven applications", "homepage": "https://nodered.org", "license": "Apache-2.0", @@ -31,10 +31,10 @@ "flow" ], "dependencies": { - "@node-red/editor-api": "4.0.2", - "@node-red/runtime": "4.0.2", - "@node-red/util": "4.0.2", - "@node-red/nodes": "4.0.2", + "@node-red/editor-api": "4.0.3", + "@node-red/runtime": "4.0.3", + "@node-red/util": "4.0.3", + "@node-red/nodes": "4.0.3", "basic-auth": "2.0.1", "bcryptjs": "2.4.3", "cors": "2.8.5", From 32540dd0e6ef97a1e0de19afb31e0ef05460eded Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:16:53 +0200 Subject: [PATCH 026/121] Fix wrong unlock state when event is triggered --- .../@node-red/editor-client/src/js/ui/deploy.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js b/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js index 25a67907c..e429c1586 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js @@ -589,6 +589,7 @@ RED.deploy = (function() { RED.notify('

' + RED._("deploy.successfulDeploy") + '

', "success"); } const flowsToLock = new Set() + // Node's properties cannot be modified if its workspace is locked. function ensureUnlocked(id) { const flow = id && (RED.nodes.workspace(id) || RED.nodes.subflow(id) || null); const isLocked = flow ? flow.locked : false; @@ -645,6 +646,9 @@ RED.deploy = (function() { RED.nodes.eachSubflow(function (subflow) { if (subflow.changed) { subflow.changed = false; + if (flowsToLock.has(subflow)) { + subflow.locked = true; + } RED.events.emit("subflows:change", subflow); } }); @@ -653,9 +657,13 @@ RED.deploy = (function() { ensureUnlocked(ws.z) ws.changed = false; delete ws.added + if (flowsToLock.has(ws)) { + ws.locked = true; + } RED.events.emit("flows:change", ws) } }); + // Ensures all workspaces/subflows to be locked have been locked. flowsToLock.forEach(flow => { flow.locked = true }) From 16c49306f35b389961ad7513bb2db792ec434bf2 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Thu, 26 Sep 2024 19:01:44 +0200 Subject: [PATCH 027/121] Fix link call node can call out of a subflow --- .../node_modules/@node-red/editor-client/src/js/nodes.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index dacd6eeae..25e8c2185 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -2408,7 +2408,7 @@ RED.nodes = (function() { } // If importing a link node, ensure both ends of each link are either: // - not in a subflow - // - both in the same subflow + // - both in the same subflow (not for link call node) if (/^link /.test(n.type) && n.links) { n.links = n.links.filter(function(id) { const otherNode = node_map[id] || RED.nodes.node(id); @@ -2419,6 +2419,9 @@ RED.nodes = (function() { if (otherNode.z === n.z) { // Both ends in the same flow/subflow return true + } else if (n.type === "link call") { + // Link call node can call out of a subflow + return true } else if (!!getSubflow(n.z) || !!getSubflow(otherNode.z)) { // One end is in a subflow - remove the link return false From cec7a86b54a60a1408704877221f1543e1d19997 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 27 Sep 2024 09:42:07 +0100 Subject: [PATCH 028/121] Update dev dependencies --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 5c41a2999..6597530f2 100644 --- a/package.json +++ b/package.json @@ -86,10 +86,10 @@ "@node-rs/bcrypt": "1.10.4" }, "devDependencies": { - "dompurify": "2.4.1", + "dompurify": "2.5.7", "grunt": "1.6.1", "grunt-chmod": "~1.1.1", - "grunt-cli": "~1.4.3", + "grunt-cli": "~1.5.0", "grunt-concurrent": "3.0.0", "grunt-contrib-clean": "2.0.1", "grunt-contrib-compress": "2.0.0", @@ -100,7 +100,7 @@ "grunt-contrib-watch": "1.1.0", "grunt-jsdoc": "2.4.1", "grunt-jsdoc-to-markdown": "6.0.0", - "grunt-jsonlint": "2.1.3", + "grunt-jsonlint": "3.0.0", "grunt-mkdir": "~1.1.0", "grunt-npm-command": "~0.1.2", "grunt-sass": "~3.1.0", @@ -114,7 +114,7 @@ "minami": "1.2.3", "mocha": "9.2.2", "node-red-node-test-helper": "^0.3.3", - "nodemon": "2.0.20", + "nodemon": "3.1.7", "proxy": "^1.0.2", "sass": "1.62.1", "should": "13.2.3", From 1053fc5121f97e1374df49353d235c21723503ff Mon Sep 17 00:00:00 2001 From: Daniel Caspi Date: Fri, 27 Sep 2024 04:35:51 -0500 Subject: [PATCH 029/121] fix typo: depreciated --- .../node_modules/@node-red/nodes/locales/en-US/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json index bc89992e2..d26f0f56b 100644 --- a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json @@ -456,7 +456,7 @@ "staticTopic": "Subscribe to single topic", "dynamicTopic": "Dynamic subscription", "auto-connect": "Connect automatically", - "auto-mode-depreciated": "This option is depreciated. Please use the new auto-detect mode.", + "auto-mode-depreciated": "This option is deprecated. Please use the new auto-detect mode.", "none": "none", "other": "other" }, From e3acc49d5e0aee41c8bab84f34c49b4cf149fef4 Mon Sep 17 00:00:00 2001 From: Ben Hardill Date: Tue, 1 Oct 2024 16:31:28 +0100 Subject: [PATCH 030/121] Allow msg.userProperties to have number values fixes #4899 --- .../node_modules/@node-red/nodes/core/network/10-mqtt.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js b/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js index db6cfba73..bc6f9644d 100644 --- a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js +++ b/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js @@ -158,9 +158,12 @@ module.exports = function(RED) { if(!keys || !keys.length) return null; keys.forEach(key => { let val = srcUserProperties[key]; - if(typeof val == "string") { + if(typeof val === "string") { count++; _clone[key] = val; + } else if (typeof val === "number") { + count++; + _clone[key] = val.toString(); } }); if(count) properties.userProperties = _clone; From 7650620a7875173152f68b1734748e376b8e7800 Mon Sep 17 00:00:00 2001 From: Joe Bordes Date: Fri, 4 Oct 2024 18:06:01 +0200 Subject: [PATCH 031/121] i18n(App) update with latest language file changes --- .../editor-client/locales/es-ES/editor.json | 55 ++++++- .../nodes/locales/es-ES/messages.json | 141 ++++++++++-------- .../nodes/locales/es-ES/network/31-tcpin.html | 2 + .../nodes/locales/es-ES/parsers/70-CSV.html | 5 +- .../runtime/locales/es-ES/runtime.json | 1 + 5 files changed, 133 insertions(+), 71 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/locales/es-ES/editor.json b/packages/node_modules/@node-red/editor-client/locales/es-ES/editor.json index 2655dfe27..eb2ef79d2 100644 --- a/packages/node_modules/@node-red/editor-client/locales/es-ES/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/es-ES/editor.json @@ -27,7 +27,8 @@ "lock": "Bloquear", "unlock": "Desbloquear", "locked": "Bloqueado", - "unlocked": "Desbloqueado" + "unlocked": "Desbloqueado", + "format": "Formato" }, "type": { "string": "texto", @@ -303,7 +304,8 @@ "missingType": "La entrada no es un flujo válido - elemento __index__ falta la propiedad 'type'" }, "conflictNotification1": "Algunos de los nodos que estás importando ya existen en tu espacio de trabajo.", - "conflictNotification2": "Selecciona qué nodos importar y si reemplazar los nodos existentes o importar una copia de los mismos." + "conflictNotification2": "Selecciona qué nodos importar y si reemplazar los nodos existentes o importar una copia de los mismos.", + "alreadyExists": "Este nodo ya existe" }, "copyMessagePath": "Ruta copiada", "copyMessageValue": "Valor copiado", @@ -371,8 +373,12 @@ "deleted": "eliminado", "flowDeleted": "flujo eliminado", "flowAdded": "flujo añadido", + "moved": "movido", "movedTo": "movido a __id__", - "movedFrom": "movido desde __id__" + "movedFrom": "movido desde __id__", + "none": "ninguno", + "position": "posición", + "wires": "conectores" }, "nodeCount": "__count__ nodo", "nodeCount_plural": "__count__ nodos", @@ -381,9 +387,14 @@ "reviewChanges": "Revisar Cambios", "noBinaryFileShowed": "No se puede mostrar el contenido del archivo binario", "viewCommitDiff": "Ver cambios de commit", + "commit": "Commit", "compareChanges": "Comparar Cambios", "saveConflict": "Guardar resolución de conflictos", "conflictHeader": "__resolved__ de __unresolved__ conflictos resueltos", + "localChanges": "Cambios Locales", + "remoteChanges": "Cambios Remotos", + "useLocalChanges": "utilizar cambios locales", + "useRemoteChanges": "utilizar cambios remotos", "commonVersionError": "La versión común no contiene JSON válido:", "oldVersionError": "La versión anterior no contiene JSON válido:", "newVersionError": "La versión nueva no contiene JSON válido:" @@ -551,7 +562,9 @@ "types": { "local": "Local", "examples": "Ejemplos" - } + }, + "type": "Tipo", + "name": "Nombre" }, "palette": { "noInfo": "no hay información disponible", @@ -613,6 +626,8 @@ }, "nodeCount": "__label__ nodo", "nodeCount_plural": "__label__ nodos", + "pluginCount": "__count__ extensión", + "pluginCount_plural": "__count__ extensiones", "moduleCount": "__count__ módulo disponible", "moduleCount_plural": "__count__ módulos disponibles", "inuse": "en uso", @@ -640,6 +655,7 @@ "errors": { "catalogLoadFailed": "

La carga del catálogo de nodos ha fallado

Revise la consola del navegador para mas información

", "installFailed": "

Fallo al instalar: __module__

__message__

Revise el log para mas información

", + "installTimeout": "

La instalación continúa en segundo plano.

Los nodos aparecerán en la paleta cuando finalice. Consulta el registro para obtener más información.

", "removeFailed": "

Fallo al eliminar: __module__

__message__

Revise el log para mas información

", "updateFailed": "

Fallo al actualizar: __module__

__message__

Revise el log para mas información

", "enableFailed": "

Fallo al activar: __module__

__message__

Revise el log para mas información

", @@ -654,6 +670,9 @@ "body":"

Eliminando '__module__'

La eliminación del nodo lo desinstalará de Node-RED. Es posible que el nodo siga utilizando recursos hasta que Node-RED sea reiniciado.

", "title": "Eliminar nodos" }, + "removePlugin": { + "body": "

Extensión __module__ eliminada. Vuelve a cargar el editor para borrar los elementos sobrantes.

" + }, "update": { "body":"

Actualizando '__module__'

La actualización del nodo requerirá un reinicio manual de Node-RED para completarse. Debe ser reiniciado manualmente.

", "title": "Actualizar nodos" @@ -665,7 +684,8 @@ "review": "Abrir información del nodo", "install": "Instalar", "remove": "Eliminar", - "update": "Actualizar" + "update": "Actualizar", + "understood": "Entendido" } } } @@ -718,6 +738,7 @@ "nodeHelp": "Ayuda de nodo", "showHelp": "Mostrar ayuda", "showInOutline": "Mostrar en controno", + "hideTopics": "Esconder temas", "showTopics": "Mostrar temas", "noHelp": "No hay ningun tema de ayuda seleccionado", "changeLog": "Registro de Cambios" @@ -792,6 +813,7 @@ "branches": "Ramas", "noBranches": "Sin ramas", "deleteConfirm": "¿Estás seguro de que quieres eliminar la rama local '__name__'? Esta acción no puede deshacerse.", + "deleteBranch": "Eliminar rama", "unmergedConfirm": "La rama local '__name__' tiene cambios no fusionados que se perderán. ¿Estás seguro de que quieres eliminarla?", "deleteUnmergedBranch": "Eliminar rama no fusionada", "gitRemotes": "Git remotes", @@ -913,6 +935,8 @@ } }, "typedInput": { + "selected": "__count__ seleccionado", + "selected_plural": "__count__ seleccionados", "type": { "str": "texto", "num": "número", @@ -923,7 +947,14 @@ "date": "marca tiempo", "jsonata": "expresión", "env": "variable de entorno", - "cred": "credencial" + "cred": "credencial", + "conf-types": "nodo configuración" + }, + "date": { + "format": { + "timestamp": "milisegundos desde epoch", + "object": "Objeto de fecha de JavaScript" + } } }, "editableList": { @@ -1205,6 +1236,18 @@ "diagnostics": { "title": "Información Sistema" }, + "languages": { + "de": "Deutsch", + "en-US": "English", + "es-ES": "Español (España)", + "fr": "Français", + "ja": "日本語", + "ko": "Korean", + "pt-BR": "Português (Brasil)", + "ru": "Русский", + "zh-CN": "简体中文", + "zh-TW": "繁體中文" + }, "validator": { "errors": { "invalid-json": "Datos JSON inválidos: __error__", diff --git a/packages/node_modules/@node-red/nodes/locales/es-ES/messages.json b/packages/node_modules/@node-red/nodes/locales/es-ES/messages.json index b8ac84f1c..19427e2fc 100644 --- a/packages/node_modules/@node-red/nodes/locales/es-ES/messages.json +++ b/packages/node_modules/@node-red/nodes/locales/es-ES/messages.json @@ -513,15 +513,15 @@ "method": "Método", "url": "URL", "doc": "Docs", - "return": "Return", - "upload": "Accept file uploads?", - "status": "Status code", - "headers": "Headers", + "return": "Devolver", + "upload": "¿Aceptar cargas de archivos?", + "status": "Código de estado", + "headers": "Encabezados", "other": "otro", "paytoqs": { - "ignore": "Ignore", - "query": "Append to query-string parameters", - "body": "Send as request body" + "ignore": "Ignorar", + "query": "Agregar a los parámetros de la cadena de consulta", + "body": "Enviar como cuerpo de la solicitud" }, "utf8String": "texto UTF8", "binaryBuffer": "buffer binario", @@ -529,45 +529,45 @@ "authType": "Tipo", "bearerToken": "Token" }, - "setby": "- set by msg.method -", - "basicauth": "Use authentication", - "use-tls": "Enable secure (SSL/TLS) connection", - "tls-config": "TLS Configuration", - "basic": "basic authentication", - "digest": "digest authentication", - "bearer": "bearer authentication", - "use-proxy": "Use proxy", - "persist": "Enable connection keep-alive", - "proxy-config": "Proxy Configuration", - "use-proxyauth": "Use proxy authentication", - "noproxy-hosts": "Ignore hosts", - "senderr": "Only send non-2xx responses to Catch node", - "utf8": "a UTF-8 string", - "binary": "a binary buffer", - "json": "a parsed JSON object", + "setby": "- establecido por msg.method -", + "basicauth": "Usar autenticación", + "use-tls": "Habilitar conexión segura (SSL/TLS)", + "tls-config": "Configuración TLS", + "basic": "autenticación básica", + "digest": "autenticación digest", + "bearer": "autenticación bearer", + "use-proxy": "Usar proxy", + "persist": "Habilitar conexión activa (keep-alive)", + "proxy-config": "Configuración Proxy", + "use-proxyauth": "Usar autenticación de proxy", + "noproxy-hosts": "Ignorar hosts", + "senderr": "Enviar solo respuestas que no sean 2xx al nodo Catch", + "utf8": "una cadena UTF-8", + "binary": "un búfer binario", + "json": "un objeto JSON analizado", "tip": { - "in": "The url will be relative to ", - "res": "The messages sent to this node must originate from an http input node", + "in": "La URL será relativa a ", + "res": "Los mensajes enviados a este nodo deben originarse desde un nodo de http input", "req": "Tip: If the JSON parse fails the fetched string is returned as-is." }, - "httpreq": "http request", + "httpreq": "solicitud http", "errors": { - "not-created": "Cannot create http-in node when httpNodeRoot set to false", - "missing-path": "missing path", - "no-response": "No response object", - "json-error": "JSON parse error", - "no-url": "No url specified", - "deprecated-call": "Deprecated call to __method__", - "invalid-transport": "non-http transport requested", - "timeout-isnan": "Timeout value is not a valid number, ignoring", - "timeout-isnegative": "Timeout value is negative, ignoring", - "invalid-payload": "Invalid payload", - "invalid-url": "Invalid url" + "not-created": "No se puede crear el nodo http-in cuando httpNodeRoot está establecido en falso", + "missing-path": "falta la ruta", + "no-response": "No hay objeto de respuesta", + "json-error": "Error de análisis en JSON", + "no-url": "No se especificó ninguna URL", + "deprecated-call": "Llamada obsoleta a __method__", + "invalid-transport": "protocolo no-http solicitado", + "timeout-isnan": "El valor de tiempo de espera no es un número válido, se ignora", + "timeout-isnegative": "El valor de tiempo de espera es negativo, se ignora", + "invalid-payload": "payload Invalido", + "invalid-url": "URL Inválida" }, "status": { - "requesting": "requesting" + "requesting": "solicitando" }, - "insecureHTTPParser": "Disable strict HTTP parsing" + "insecureHTTPParser": "Deshabilitar el análisis estricto de HTTP" }, "websocket": { "label": { @@ -576,41 +576,42 @@ "url": "URL", "subprotocol": "Subprotocolo" }, - "listenon": "Listen on", - "connectto": "Connect to", - "sendrec": "Send/Receive", + "listenon": "Escuchar", + "connectto": "Conectar a", + "sendrec": "Enviar/Recibir", "payload": "payload", - "message": "entire message", - "sendheartbeat": "Send heartbeat", + "message": "mensaje completo", + "sendheartbeat": "Enviar latido", "tip": { - "path1": "By default, payload will contain the data to be sent over, or received from a websocket. The listener can be configured to send or receive the entire message object as a JSON formatted string.", - "path2": "This path will be relative to __path__.", - "url1": "URL should use ws:// or wss:// scheme and point to an existing websocket listener.", - "url2": "By default, payload will contain the data to be sent over, or received from a websocket. The client can be configured to send or receive the entire message object as a JSON formatted string." + "path1": "De manera predeterminada, payload contendrá los datos que se enviarán o recibirán de un websocket. El receptor puede configurarse para enviar o recibir el objeto de mensaje completo como una cadena en formato JSON.", + "path2": "Esta ruta será relativa a __path__.", + "url1": "La URL debe usar el esquema ws:// o wss:// y apuntar a un receptor de websocket existente.", + "url2": "De manera predeterminada, payload contendrá los datos que se enviarán o recibirán de un websocket. El cliente puede configurarse para enviar o recibir el objeto de mensaje completo como una cadena en formato JSON", + "headers": "Los encabezados solo se envían durante el mecanismo de actualización del protocolo, de HTTP al protocolo WS/WSS." }, "status": { - "connected": "connected __count__", - "connected_plural": "connected __count__" + "connected": "__count__ conectado", + "connected_plural": "__count__ conectados" }, "errors": { - "connect-error": "An error occurred on the ws connection: ", - "send-error": "An error occurred while sending: ", - "missing-conf": "Missing server configuration", - "duplicate-path": "Cannot have two WebSocket listeners on the same path: __path__", - "missing-server": "Missing server configuration", - "missing-client": "Missing client configuration" + "connect-error": "Se produjo un error en la conexión ws:", + "send-error": "Se produjo un error al enviar: ", + "missing-conf": "Falta la configuración del servidor", + "duplicate-path": "No se pueden tener dos escuchas de WebSocket en la misma ruta: __path__", + "missing-server": "Falta la configuración del servidor", + "missing-client": "Falta la configuración del cliente" } }, "watch": { - "watch": "watch", + "watch": "observar", "label": { - "files": "File(s)", - "recursive": "Watch sub-directories recursively" + "files": "Fichero(s)", + "recursive": "Observar subdirectorios recursivamente" }, "placeholder": { - "files": "Comma-separated list of files and/or directories" + "files": "Lista de archivos y/o directorios separados por comas" }, - "tip": "On Windows you must use double back-slashes \\\\ in any directory names." + "tip": "En Windows, debes utilizar barras invertidas dobles \\\\ en cualquier nombre de directorio." }, "tcpin": { "label": { @@ -849,7 +850,13 @@ "newline": "Nueva línea", "usestrings": "analizar valores numéricos", "include_empty_strings": "incluir cadenas vacías", - "include_null_values": "incluir valores nulos" + "include_null_values": "incluir valores nulos", + "spec": "Analizador" + }, + "spec": { + "rfc": "RFC4180", + "legacy": "Legado", + "legacy_warning": "El modo legado se eliminará en una versión futura." }, "placeholder": { "columns": "nombres de columnas separados por comas" @@ -878,6 +885,7 @@ "once": "enviar encabezados una vez, hasta msg.reset" }, "errors": { + "bad_template": "Plantilla de columnas mal formada.", "csv_js": "Este nodo solo maneja cadenas CSV u objetos JS.", "obj_csv": "No se ha especificado ninguna plantilla de columnas para el objeto -> CSV.", "bad_csv": "Datos CSV con formato incorrecto: la salida probablemente esté corrupta." @@ -887,12 +895,14 @@ "label": { "select": "Selector", "output": "Salida", - "in": "en" + "in": "en", + "prefix": "Nombre de la propiedad para el contenido HTML" }, "output": { "html": "el contenido HTML de los elementos", "text": "sólo el contenido textual de los elementos", - "attr": "un objeto de cualquier atributo de los elementos" + "attr": "un objeto de cualquier atributo de los elementos", + "compl": "un objeto de cualquier atributo de los elementos y contenidos html" }, "format": { "single": "como un mensaje único que contiene una matriz", @@ -1007,6 +1017,7 @@ "objectSend": "Enviar un mensaje para cada par clave/valor", "strBuff": "Texto / Buffer", "array": "Array", + "splitThe": "Dividir el", "splitUsing": "Dividir usando", "splitLength": "Longitud fija de", "stream": "Manejar como un flujo de mensajes", @@ -1036,6 +1047,7 @@ "joinedUsing": "se unió usando", "send": "Enviar el mensaje:", "afterCount": "Después de varias partes del mensaje", + "useparts": "Usar la propiedad msg.parts existente", "count": "contar", "subsequent": "y cada mensaje posterior.", "afterTimeout": "Después de un tiempo de espera trás el primer mensaje", @@ -1102,6 +1114,7 @@ "too-many": "demasiados mensajes pendientes en el nodo de lotes", "unexpected": "modo inesperado", "no-parts": "ninguna propiedad 'parte' en el mensaje", + "honourParts": "Permitir que msg.parts también complete la operación por lotes.", "error": { "invalid-count": "Recuento no válido", "invalid-overlap": "Solapamiento no válido", diff --git a/packages/node_modules/@node-red/nodes/locales/es-ES/network/31-tcpin.html b/packages/node_modules/@node-red/nodes/locales/es-ES/network/31-tcpin.html index 0d25594b4..d9e07902a 100644 --- a/packages/node_modules/@node-red/nodes/locales/es-ES/network/31-tcpin.html +++ b/packages/node_modules/@node-red/nodes/locales/es-ES/network/31-tcpin.html @@ -24,12 +24,14 @@

Solo se envía el msg.payload.

Si msg.payload es una cadena que contiene una codificación Base64 de datos binarios, la opción de decodificación Base64 hará que se vuelva a convertir a binario antes de enviarse.

Si msg._session no está presente, la carga se envía a todos los clientes conectados.

+

En el modo Responder a, configurar msg.reset = true restablecerá la conexión especificada por _session.id, o todas las conexiones si no se especifica _session.id.

Nota: En algunos sistemas, es posible que necesites acceso raíz o de administrador para acceder a los puertos inferiores a 1024.

diff --git a/packages/node_modules/@node-red/nodes/locales/es-ES/parsers/70-CSV.html b/packages/node_modules/@node-red/nodes/locales/es-ES/parsers/70-CSV.html index 5dbbb88f0..940c65dfb 100644 --- a/packages/node_modules/@node-red/nodes/locales/es-ES/parsers/70-CSV.html +++ b/packages/node_modules/@node-red/nodes/locales/es-ES/parsers/70-CSV.html @@ -35,7 +35,9 @@

Detalles

-

La plantilla de columnas puede contener una lista ordenada de nombres de columnas. Al convertir CSV en un objeto, los nombres de las columnas se utilizarán como nombres de propiedades. Alternativamente, los nombres de las columnas se pueden tomar de la primera fila del CSV.

+

La plantilla de columnas puede contener una lista ordenada de nombres de columnas. Al convertir CSV en un objeto, los nombres de las columnas se utilizarán como nombres de propiedades. Alternativamente, los nombres de las columnas se pueden tomar de la primera fila del CSV. +

Cuando se selecciona el analizador RFC, la plantilla de columna debe ser compatible con RFC4180.

+

Al convertir a CSV, la plantilla de columnas se utiliza para identificar qué propiedades extraer del objeto y en qué orden.

Si la plantilla de columnas está en blanco, puede utilizar una lista simple de propiedades separadas por comas proporcionada en msg.columns para determinar qué extraer y en qué orden. Si ninguno de los dos está presente, todas las propiedades del objeto se muestran en el orden en que se encuentran en la primera fila.

Si la entrada es una matriz, entonces la plantilla de columnas solo se usa para generar opcionalmente una fila de títulos de columnas.

@@ -46,4 +48,5 @@

Si genera varios mensajes, tendrán su propiedad parts configurada y formarán una secuencia de mensajes completa.

Si el nodo está configurado para enviar encabezados de columna solo una vez, si se configura msg.reset en cualquier valor hará que el nodo reenvíe los encabezados.

Nota: la plantilla de columna debe estar separada por comas, incluso si se elige un separador diferente para los datos.

+

Nota: en el modo RFC, se generarán errores detectables para encabezados CSV mal formados y datos de carga útil de entrada no válidos

diff --git a/packages/node_modules/@node-red/runtime/locales/es-ES/runtime.json b/packages/node_modules/@node-red/runtime/locales/es-ES/runtime.json index 9fba64ad3..d1c2c44a9 100644 --- a/packages/node_modules/@node-red/runtime/locales/es-ES/runtime.json +++ b/packages/node_modules/@node-red/runtime/locales/es-ES/runtime.json @@ -25,6 +25,7 @@ "removing-modules": "Eliminando módulos de la configuración", "added-types": "Tipos de nodos añadidos:", "removed-types": "Tipos de nodos eliminados:", + "removed-plugins": "Extensiones eliminadas:", "install": { "invalid": "Nombre de módulo no válido", "installing": "Instalando módulo: __name__, versión: __version__", From bdb545d6ebd438189010ea55bd7ffba2c4a41bf8 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 7 Oct 2024 16:30:38 +0100 Subject: [PATCH 032/121] Update dependencies --- package.json | 14 +++++++------- packages/node_modules/@node-red/nodes/package.json | 6 +++--- .../node_modules/@node-red/registry/package.json | 4 ++-- packages/node_modules/@node-red/util/package.json | 2 +- packages/node_modules/node-red/package.json | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 6597530f2..952b95bc0 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "cheerio": "1.0.0-rc.10", "clone": "2.1.2", "content-type": "1.0.5", - "cookie": "0.6.0", + "cookie": "0.7.2", "cookie-parser": "1.4.6", "cors": "2.8.5", "cronosjs": "1.7.1", @@ -45,7 +45,7 @@ "express-session": "1.18.0", "form-data": "4.0.0", "fs-extra": "11.2.0", - "got": "12.6.0", + "got": "12.6.1", "hash-sum": "2.0.0", "hpagent": "1.2.0", "https-proxy-agent": "5.0.1", @@ -60,7 +60,7 @@ "memorystore": "1.6.7", "mime": "3.0.0", "moment": "2.30.1", - "moment-timezone": "0.5.45", + "moment-timezone": "0.5.46", "mqtt": "5.7.0", "multer": "1.4.5-lts.1", "mustache": "4.2.0", @@ -72,10 +72,10 @@ "passport": "0.7.0", "passport-http-bearer": "1.0.1", "passport-oauth2-client-password": "0.1.2", - "raw-body": "2.5.2", + "raw-body": "3.0.0", "rfdc": "^1.3.1", - "semver": "7.5.4", - "tar": "7.2.0", + "semver": "7.6.3", + "tar": "7.4.3", "tough-cookie": "^5.0.0", "uglify-js": "3.17.4", "uuid": "9.0.1", @@ -110,7 +110,7 @@ "jquery-i18next": "1.2.1", "jsdoc-nr-template": "github:node-red/jsdoc-nr-template", "marked": "4.3.0", - "mermaid": "^10.4.0", + "mermaid": "11.3.0", "minami": "1.2.3", "mocha": "9.2.2", "node-red-node-test-helper": "^0.3.3", diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json index dcbb59b24..72179409c 100644 --- a/packages/node_modules/@node-red/nodes/package.json +++ b/packages/node_modules/@node-red/nodes/package.json @@ -22,13 +22,13 @@ "cheerio": "1.0.0-rc.10", "content-type": "1.0.5", "cookie-parser": "1.4.6", - "cookie": "0.6.0", + "cookie": "0.7.2", "cors": "2.8.5", "cronosjs": "1.7.1", "denque": "2.1.0", "form-data": "4.0.0", "fs-extra": "11.2.0", - "got": "12.6.0", + "got": "12.6.1", "hash-sum": "2.0.0", "hpagent": "1.2.0", "https-proxy-agent": "5.0.1", @@ -40,7 +40,7 @@ "mustache": "4.2.0", "node-watch": "0.7.4", "on-headers": "1.0.2", - "raw-body": "2.5.2", + "raw-body": "3.0.0", "tough-cookie": "^5.0.0", "uuid": "9.0.1", "ws": "7.5.10", diff --git a/packages/node_modules/@node-red/registry/package.json b/packages/node_modules/@node-red/registry/package.json index 2515b19b7..7c6e8ebcc 100644 --- a/packages/node_modules/@node-red/registry/package.json +++ b/packages/node_modules/@node-red/registry/package.json @@ -19,8 +19,8 @@ "@node-red/util": "4.0.3", "clone": "2.1.2", "fs-extra": "11.2.0", - "semver": "7.5.4", - "tar": "7.2.0", + "semver": "7.6.3", + "tar": "7.4.3", "uglify-js": "3.17.4" } } diff --git a/packages/node_modules/@node-red/util/package.json b/packages/node_modules/@node-red/util/package.json index d747109dd..67ed9d88b 100644 --- a/packages/node_modules/@node-red/util/package.json +++ b/packages/node_modules/@node-red/util/package.json @@ -21,6 +21,6 @@ "jsonata": "2.0.5", "lodash.clonedeep": "^4.5.0", "moment": "2.30.1", - "moment-timezone": "0.5.45" + "moment-timezone": "0.5.46" } } diff --git a/packages/node_modules/node-red/package.json b/packages/node_modules/node-red/package.json index b6c767e9b..2f61e8468 100644 --- a/packages/node_modules/node-red/package.json +++ b/packages/node_modules/node-red/package.json @@ -42,7 +42,7 @@ "fs-extra": "11.2.0", "node-red-admin": "^4.0.1", "nopt": "5.0.0", - "semver": "7.5.4" + "semver": "7.6.3" }, "optionalDependencies": { "@node-rs/bcrypt": "1.10.4" From 3812ed5ed3cd7b51b074025ae8eacdda31e6762e Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Mon, 7 Oct 2024 21:13:13 +0200 Subject: [PATCH 033/121] Link call node cannot call a link in a subflow Co-authored-by: Nick O'Leary --- .../node_modules/@node-red/editor-client/src/js/nodes.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 25e8c2185..857068d4b 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -2419,12 +2419,13 @@ RED.nodes = (function() { if (otherNode.z === n.z) { // Both ends in the same flow/subflow return true - } else if (n.type === "link call") { - // Link call node can call out of a subflow - return true + } else if (n.type === "link call" && !!getSubflow(otherNode.z)) { + // Link call node can call out of a subflow as long as otherNode is + // not in a subflow + return false } else if (!!getSubflow(n.z) || !!getSubflow(otherNode.z)) { // One end is in a subflow - remove the link - return false + return false } return true }); From a0b4fc83726e647cfc2a6f6db2a529675d10785b Mon Sep 17 00:00:00 2001 From: Ben Hardill Date: Tue, 8 Oct 2024 13:58:29 +0100 Subject: [PATCH 034/121] Convert to string --- .../node_modules/@node-red/nodes/core/network/10-mqtt.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js b/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js index bc6f9644d..2b4968483 100644 --- a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js +++ b/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js @@ -161,9 +161,13 @@ module.exports = function(RED) { if(typeof val === "string") { count++; _clone[key] = val; - } else if (typeof val === "number") { + } else { count++; - _clone[key] = val.toString(); + try { + _clone[key] = JSON.stringify(val) + } catch (err) { + console.log('MQTT v5 Property value can not be converted to a String') + } } }); if(count) properties.userProperties = _clone; From d50ccea017e97552078a86324c76159957b66614 Mon Sep 17 00:00:00 2001 From: Ben Hardill Date: Tue, 8 Oct 2024 16:51:09 +0100 Subject: [PATCH 035/121] Update packages/node_modules/@node-red/nodes/core/network/10-mqtt.js Co-authored-by: Nick O'Leary --- .../node_modules/@node-red/nodes/core/network/10-mqtt.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js b/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js index 2b4968483..afa0066f4 100644 --- a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js +++ b/packages/node_modules/@node-red/nodes/core/network/10-mqtt.js @@ -161,12 +161,12 @@ module.exports = function(RED) { if(typeof val === "string") { count++; _clone[key] = val; - } else { - count++; + } else if (val !== undefined && val !== null) { try { _clone[key] = JSON.stringify(val) + count++; } catch (err) { - console.log('MQTT v5 Property value can not be converted to a String') + // Silently drop property } } }); From 49a3eded59e59b6dc1207fcd872233aa0cb2e701 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Tue, 8 Oct 2024 18:10:28 +0200 Subject: [PATCH 036/121] Apply code review + add comments Co-authored-by: Nick O'Leary --- .../@node-red/editor-client/src/js/ui/deploy.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js b/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js index e429c1586..ed503bcfe 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js @@ -591,6 +591,7 @@ RED.deploy = (function() { const flowsToLock = new Set() // Node's properties cannot be modified if its workspace is locked. function ensureUnlocked(id) { + // TODO: `RED.nodes.subflow` is useless const flow = id && (RED.nodes.workspace(id) || RED.nodes.subflow(id) || null); const isLocked = flow ? flow.locked : false; if (flow && isLocked) { @@ -643,27 +644,27 @@ RED.deploy = (function() { delete confNode.credentials; } }); + // Subflow cannot be locked RED.nodes.eachSubflow(function (subflow) { if (subflow.changed) { subflow.changed = false; - if (flowsToLock.has(subflow)) { - subflow.locked = true; - } RED.events.emit("subflows:change", subflow); } }); RED.nodes.eachWorkspace(function (ws) { if (ws.changed || ws.added) { - ensureUnlocked(ws.z) + // Ensure the Workspace is unlocked to modify its properties. + ensureUnlocked(ws.id); ws.changed = false; delete ws.added if (flowsToLock.has(ws)) { ws.locked = true; + flowsToLock.delete(ws); } RED.events.emit("flows:change", ws) } }); - // Ensures all workspaces/subflows to be locked have been locked. + // Ensures all workspaces to be locked have been locked. flowsToLock.forEach(flow => { flow.locked = true }) From a0033697ea435fdd69db51e8edb17439de5eba5a Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 9 Oct 2024 10:25:35 +0100 Subject: [PATCH 037/121] Update cookie --- package.json | 6 +++--- packages/node_modules/@node-red/editor-api/package.json | 4 ++-- packages/node_modules/@node-red/nodes/package.json | 2 +- packages/node_modules/@node-red/runtime/package.json | 2 +- packages/node_modules/node-red/package.json | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 952b95bc0..229ede53d 100644 --- a/package.json +++ b/package.json @@ -37,12 +37,12 @@ "clone": "2.1.2", "content-type": "1.0.5", "cookie": "0.7.2", - "cookie-parser": "1.4.6", + "cookie-parser": "1.4.7", "cors": "2.8.5", "cronosjs": "1.7.1", "denque": "2.1.0", - "express": "4.21.0", - "express-session": "1.18.0", + "express": "4.21.1", + "express-session": "1.18.1", "form-data": "4.0.0", "fs-extra": "11.2.0", "got": "12.6.1", diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json index 59a265ca4..02d8cf673 100644 --- a/packages/node_modules/@node-red/editor-api/package.json +++ b/packages/node_modules/@node-red/editor-api/package.json @@ -22,8 +22,8 @@ "body-parser": "1.20.3", "clone": "2.1.2", "cors": "2.8.5", - "express-session": "1.18.0", - "express": "4.21.0", + "express-session": "1.18.1", + "express": "4.21.1", "memorystore": "1.6.7", "mime": "3.0.0", "multer": "1.4.5-lts.1", diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json index 72179409c..a2f82334a 100644 --- a/packages/node_modules/@node-red/nodes/package.json +++ b/packages/node_modules/@node-red/nodes/package.json @@ -21,7 +21,7 @@ "body-parser": "1.20.3", "cheerio": "1.0.0-rc.10", "content-type": "1.0.5", - "cookie-parser": "1.4.6", + "cookie-parser": "1.4.7", "cookie": "0.7.2", "cors": "2.8.5", "cronosjs": "1.7.1", diff --git a/packages/node_modules/@node-red/runtime/package.json b/packages/node_modules/@node-red/runtime/package.json index 67464f496..276e2ecef 100644 --- a/packages/node_modules/@node-red/runtime/package.json +++ b/packages/node_modules/@node-red/runtime/package.json @@ -20,7 +20,7 @@ "@node-red/util": "4.0.3", "async-mutex": "0.5.0", "clone": "2.1.2", - "express": "4.21.0", + "express": "4.21.1", "fs-extra": "11.2.0", "json-stringify-safe": "5.0.1", "rfdc": "^1.3.1" diff --git a/packages/node_modules/node-red/package.json b/packages/node_modules/node-red/package.json index 2f61e8468..b3fab303d 100644 --- a/packages/node_modules/node-red/package.json +++ b/packages/node_modules/node-red/package.json @@ -38,7 +38,7 @@ "basic-auth": "2.0.1", "bcryptjs": "2.4.3", "cors": "2.8.5", - "express": "4.21.0", + "express": "4.21.1", "fs-extra": "11.2.0", "node-red-admin": "^4.0.1", "nopt": "5.0.0", From 90ea3c15b35e042f285aecf1fc7e234738f0ed19 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 9 Oct 2024 10:56:14 +0100 Subject: [PATCH 038/121] Bump for 4.0.4 release --- CHANGELOG.md | 17 +++++++++++++++++ package.json | 2 +- .../@node-red/editor-api/package.json | 6 +++--- .../@node-red/editor-client/package.json | 2 +- .../node_modules/@node-red/nodes/package.json | 2 +- .../@node-red/registry/package.json | 4 ++-- .../node_modules/@node-red/runtime/package.json | 6 +++--- .../node_modules/@node-red/util/package.json | 2 +- packages/node_modules/node-red/package.json | 10 +++++----- 9 files changed, 34 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6eaa3b7fa..5d51c45f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +#### 4.0.4: Maintenance Release + +Editor + + - Fix `link call` node can call out of a subflow (#4892) @GogoVega + - Fix wrong unlock state when event is triggered after deployment (#4889) @GogoVega + - i18n(App) update with latest language file changes (#4903) @joebordes + - fix typo: depreciated (#4895) @dxdc + +Runtime + + - Update dev dependencies (#4893) @knolleary + +Nodes + + - MQTT: Allow msg.userProperties to have number values (#4900) @hardillb + #### 4.0.3: Maintenance Release Editor diff --git a/package.json b/package.json index 229ede53d..9d30315fd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "4.0.3", + "version": "4.0.4", "description": "Low-code programming for event-driven applications", "homepage": "https://nodered.org", "license": "Apache-2.0", diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json index 02d8cf673..b1cdd9ad5 100644 --- a/packages/node_modules/@node-red/editor-api/package.json +++ b/packages/node_modules/@node-red/editor-api/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-api", - "version": "4.0.3", + "version": "4.0.4", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/util": "4.0.3", - "@node-red/editor-client": "4.0.3", + "@node-red/util": "4.0.4", + "@node-red/editor-client": "4.0.4", "bcryptjs": "2.4.3", "body-parser": "1.20.3", "clone": "2.1.2", diff --git a/packages/node_modules/@node-red/editor-client/package.json b/packages/node_modules/@node-red/editor-client/package.json index 7e65dcc1a..587a32c6a 100644 --- a/packages/node_modules/@node-red/editor-client/package.json +++ b/packages/node_modules/@node-red/editor-client/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-client", - "version": "4.0.3", + "version": "4.0.4", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json index a2f82334a..7fdcb3a3e 100644 --- a/packages/node_modules/@node-red/nodes/package.json +++ b/packages/node_modules/@node-red/nodes/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/nodes", - "version": "4.0.3", + "version": "4.0.4", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/registry/package.json b/packages/node_modules/@node-red/registry/package.json index 7c6e8ebcc..229c5d31d 100644 --- a/packages/node_modules/@node-red/registry/package.json +++ b/packages/node_modules/@node-red/registry/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/registry", - "version": "4.0.3", + "version": "4.0.4", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,7 +16,7 @@ } ], "dependencies": { - "@node-red/util": "4.0.3", + "@node-red/util": "4.0.4", "clone": "2.1.2", "fs-extra": "11.2.0", "semver": "7.6.3", diff --git a/packages/node_modules/@node-red/runtime/package.json b/packages/node_modules/@node-red/runtime/package.json index 276e2ecef..71d36b54a 100644 --- a/packages/node_modules/@node-red/runtime/package.json +++ b/packages/node_modules/@node-red/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/runtime", - "version": "4.0.3", + "version": "4.0.4", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/registry": "4.0.3", - "@node-red/util": "4.0.3", + "@node-red/registry": "4.0.4", + "@node-red/util": "4.0.4", "async-mutex": "0.5.0", "clone": "2.1.2", "express": "4.21.1", diff --git a/packages/node_modules/@node-red/util/package.json b/packages/node_modules/@node-red/util/package.json index 67ed9d88b..80ae1a517 100644 --- a/packages/node_modules/@node-red/util/package.json +++ b/packages/node_modules/@node-red/util/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/util", - "version": "4.0.3", + "version": "4.0.4", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/node-red/package.json b/packages/node_modules/node-red/package.json index b3fab303d..b7e87be84 100644 --- a/packages/node_modules/node-red/package.json +++ b/packages/node_modules/node-red/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "4.0.3", + "version": "4.0.4", "description": "Low-code programming for event-driven applications", "homepage": "https://nodered.org", "license": "Apache-2.0", @@ -31,10 +31,10 @@ "flow" ], "dependencies": { - "@node-red/editor-api": "4.0.3", - "@node-red/runtime": "4.0.3", - "@node-red/util": "4.0.3", - "@node-red/nodes": "4.0.3", + "@node-red/editor-api": "4.0.4", + "@node-red/runtime": "4.0.4", + "@node-red/util": "4.0.4", + "@node-red/nodes": "4.0.4", "basic-auth": "2.0.1", "bcryptjs": "2.4.3", "cors": "2.8.5", From 802b116b0140f8b06da7087b8de07c9f68139b28 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Thu, 10 Oct 2024 08:53:33 +0200 Subject: [PATCH 039/121] Refix link call node can call out of a subflow --- packages/node_modules/@node-red/editor-client/src/js/nodes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 857068d4b..12e0a6ef1 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -2419,10 +2419,10 @@ RED.nodes = (function() { if (otherNode.z === n.z) { // Both ends in the same flow/subflow return true - } else if (n.type === "link call" && !!getSubflow(otherNode.z)) { + } else if (n.type === "link call" && !getSubflow(otherNode.z)) { // Link call node can call out of a subflow as long as otherNode is // not in a subflow - return false + return true } else if (!!getSubflow(n.z) || !!getSubflow(otherNode.z)) { // One end is in a subflow - remove the link return false From fe9354d10b893de689efaddaf120c690bb595049 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 10 Oct 2024 11:05:08 +0100 Subject: [PATCH 040/121] Bump for 4.0.5 --- CHANGELOG.md | 6 ++++++ package.json | 2 +- .../node_modules/@node-red/editor-api/package.json | 6 +++--- .../node_modules/@node-red/editor-client/package.json | 2 +- packages/node_modules/@node-red/nodes/package.json | 2 +- packages/node_modules/@node-red/registry/package.json | 4 ++-- packages/node_modules/@node-red/runtime/package.json | 6 +++--- packages/node_modules/@node-red/util/package.json | 2 +- packages/node_modules/node-red/package.json | 10 +++++----- scripts/generate-publish-script.js | 8 +++++--- 10 files changed, 28 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d51c45f9..4b55ac4c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +#### 4.0.5: Maintenance Release + +Editor + + - Refix link call node can call out of a subflow (#4908) @GogoVega + #### 4.0.4: Maintenance Release Editor diff --git a/package.json b/package.json index 9d30315fd..c7fe644f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "4.0.4", + "version": "4.0.5", "description": "Low-code programming for event-driven applications", "homepage": "https://nodered.org", "license": "Apache-2.0", diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json index b1cdd9ad5..e58f2e9fb 100644 --- a/packages/node_modules/@node-red/editor-api/package.json +++ b/packages/node_modules/@node-red/editor-api/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-api", - "version": "4.0.4", + "version": "4.0.5", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/util": "4.0.4", - "@node-red/editor-client": "4.0.4", + "@node-red/util": "4.0.5", + "@node-red/editor-client": "4.0.5", "bcryptjs": "2.4.3", "body-parser": "1.20.3", "clone": "2.1.2", diff --git a/packages/node_modules/@node-red/editor-client/package.json b/packages/node_modules/@node-red/editor-client/package.json index 587a32c6a..b6b7a2c73 100644 --- a/packages/node_modules/@node-red/editor-client/package.json +++ b/packages/node_modules/@node-red/editor-client/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-client", - "version": "4.0.4", + "version": "4.0.5", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json index 7fdcb3a3e..84db88466 100644 --- a/packages/node_modules/@node-red/nodes/package.json +++ b/packages/node_modules/@node-red/nodes/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/nodes", - "version": "4.0.4", + "version": "4.0.5", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/registry/package.json b/packages/node_modules/@node-red/registry/package.json index 229c5d31d..9437a5beb 100644 --- a/packages/node_modules/@node-red/registry/package.json +++ b/packages/node_modules/@node-red/registry/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/registry", - "version": "4.0.4", + "version": "4.0.5", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,7 +16,7 @@ } ], "dependencies": { - "@node-red/util": "4.0.4", + "@node-red/util": "4.0.5", "clone": "2.1.2", "fs-extra": "11.2.0", "semver": "7.6.3", diff --git a/packages/node_modules/@node-red/runtime/package.json b/packages/node_modules/@node-red/runtime/package.json index 71d36b54a..a160c402c 100644 --- a/packages/node_modules/@node-red/runtime/package.json +++ b/packages/node_modules/@node-red/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/runtime", - "version": "4.0.4", + "version": "4.0.5", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/registry": "4.0.4", - "@node-red/util": "4.0.4", + "@node-red/registry": "4.0.5", + "@node-red/util": "4.0.5", "async-mutex": "0.5.0", "clone": "2.1.2", "express": "4.21.1", diff --git a/packages/node_modules/@node-red/util/package.json b/packages/node_modules/@node-red/util/package.json index 80ae1a517..c02ab3805 100644 --- a/packages/node_modules/@node-red/util/package.json +++ b/packages/node_modules/@node-red/util/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/util", - "version": "4.0.4", + "version": "4.0.5", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/node-red/package.json b/packages/node_modules/node-red/package.json index b7e87be84..0969024ff 100644 --- a/packages/node_modules/node-red/package.json +++ b/packages/node_modules/node-red/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "4.0.4", + "version": "4.0.5", "description": "Low-code programming for event-driven applications", "homepage": "https://nodered.org", "license": "Apache-2.0", @@ -31,10 +31,10 @@ "flow" ], "dependencies": { - "@node-red/editor-api": "4.0.4", - "@node-red/runtime": "4.0.4", - "@node-red/util": "4.0.4", - "@node-red/nodes": "4.0.4", + "@node-red/editor-api": "4.0.5", + "@node-red/runtime": "4.0.5", + "@node-red/util": "4.0.5", + "@node-red/nodes": "4.0.5", "basic-auth": "2.0.1", "bcryptjs": "2.4.3", "cors": "2.8.5", diff --git a/scripts/generate-publish-script.js b/scripts/generate-publish-script.js index 0b2a75cd0..c31b2dd9e 100644 --- a/scripts/generate-publish-script.js +++ b/scripts/generate-publish-script.js @@ -36,10 +36,12 @@ function generateScript() { packages.forEach(name => { const tarName = name.replace(/@/,"").replace(/\//,"-") lines.push(`npm publish ${tarName}-${version}.tgz ${tagArg}\n`); - if (updateNextToLatest) { - lines.push(`npm dist-tag add ${name}@${version} next\n`); - } }) + if (updateNextToLatest) { + packages.forEach(name => { + lines.push(`npm dist-tag add ${name}@${version} next\n`); + }) + } resolve(lines.join("")) }); } From a7b1ce0cf84acf25d1517af1be2ff0b06e5293b5 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:47:42 +0200 Subject: [PATCH 041/121] Improve the node name auto-generated with the first available nb --- .../editor-client/src/js/ui/view-tools.js | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js index f503beecb..22cb1eecd 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js @@ -1102,18 +1102,27 @@ RED.view.tools = (function() { const paletteLabel = RED.utils.getPaletteLabel(n.type, nodeDef) const defaultNodeNameRE = new RegExp('^'+paletteLabel.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')+' (\\d+)$') if (!typeIndex.hasOwnProperty(n.type)) { - const existingNodes = RED.nodes.filterNodes({type: n.type}) - let maxNameNumber = 0; - existingNodes.forEach(n => { - let match = defaultNodeNameRE.exec(n.name) + const existingNodes = RED.nodes.filterNodes({ type: n.type }); + const existingIds = existingNodes.reduce((ids, node) => { + let match = defaultNodeNameRE.exec(node.name); if (match) { - let nodeNumber = parseInt(match[1]) - if (nodeNumber > maxNameNumber) { - maxNameNumber = nodeNumber + const nodeNumber = parseInt(match[1], 10); + if (!ids.includes(nodeNumber)) { + ids.push(nodeNumber); } } - }) - typeIndex[n.type] = maxNameNumber + 1 + return ids; + }, []).sort((a, b) => a - b); + + let availableNameNumber = 1; + for (let i = 0; i < existingIds.length; i++) { + if (existingIds[i] !== availableNameNumber) { + break; + } + availableNameNumber++; + } + + typeIndex[n.type] = availableNameNumber; } if ((options.renameBlank && n.name === '') || (options.renameClash && defaultNodeNameRE.test(n.name))) { if (generateHistory) { From 2eba7548013a0bfba56369abc1bc58f61f61d3ca Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Sat, 12 Oct 2024 16:49:09 +0100 Subject: [PATCH 042/121] Fix trigger node date handling for latest time type input to fix #4914 --- .../@node-red/nodes/core/function/89-trigger.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/function/89-trigger.js b/packages/node_modules/@node-red/nodes/core/function/89-trigger.js index 16a00e99d..0fc6ad6fb 100644 --- a/packages/node_modules/@node-red/nodes/core/function/89-trigger.js +++ b/packages/node_modules/@node-red/nodes/core/function/89-trigger.js @@ -166,7 +166,13 @@ module.exports = function(RED) { if (err) { reject(err); } else { - msg.payload = value; + if (node.op1type === "date") { + let d = new Date(); + if (node.op1 == "1") { msg.payload = Date.now(); } + else if (node.op1 == "iso") { msg.payload = d.toISOString(); } + else { msg.payload = d; } + } + else { msg.payload = value; } resolve(); } }); @@ -213,7 +219,12 @@ module.exports = function(RED) { } else { msg2.payload = node.topics[topic].m2; - if (node.op2type === "date") { msg2.payload = Date.now(); } + if (node.op2type === "date") { + let d = new Date(); + if (node.op2 == "0") { msg2.payload = Date.now(); } + else if (node.op2 == "iso") { msg2.payload = d.toISOString(); } + else { msg2.payload = d; } + } if (node.second === true) { msgInfo.send([null,msg2]); } else { msgInfo.send(msg2); } } From 61fd01b8716e62d5f26fbcc1d56f47ca6d05db5d Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Sat, 12 Oct 2024 17:09:18 +0100 Subject: [PATCH 043/121] And add some tests --- test/nodes/core/function/89-trigger_spec.js | 24 +++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/test/nodes/core/function/89-trigger_spec.js b/test/nodes/core/function/89-trigger_spec.js index 33401e540..e86ed7a31 100644 --- a/test/nodes/core/function/89-trigger_spec.js +++ b/test/nodes/core/function/89-trigger_spec.js @@ -111,7 +111,15 @@ describe('trigger node', function() { try { if (rval) { msg.should.have.property("payload"); - should.deepEqual(msg.payload, rval); + if (type == "date" && val == "1") { + should.deepEqual(Math.round(msg.payload/10000), Math.round(rval/10000)); + } + else if (type == "date" && val == "iso") { + should.deepEqual(msg.payload.substr(0,11), rval.substr(0,11)); + } + else { + should.deepEqual(msg.payload, rval); + } } else { msg.should.have.property("payload", val); @@ -126,6 +134,7 @@ describe('trigger node', function() { }); it('should output 2st value when triggered ('+type+')', function(done) { + if (type == "date" && val == "1") { val = "0"; } var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", op1:"foo", op1type:"str", op2:val, op2type:type, duration:"20", wires:[["n2"]] }, {id:"n2", type:"helper"} ]; process.env[val] = rval; @@ -142,7 +151,15 @@ describe('trigger node', function() { else { if (rval) { msg.should.have.property("payload"); - should.deepEqual(msg.payload, rval); + if (type == "date" && val == "0") { + should.deepEqual(Math.round(msg.payload/10000), Math.round(rval/10000)); + } + else if (type == "date" && val == "iso") { + should.deepEqual(msg.payload.substr(0,11), rval.substr(0,11)); + } + else { + should.deepEqual(msg.payload, rval); + } } else { msg.should.have.property("payload", val); @@ -166,6 +183,9 @@ describe('trigger node', function() { var val_buf = "[1,2,3,4,5]"; basicTest("bin", val_buf, Buffer.from(JSON.parse(val_buf))); basicTest("env", "NR-TEST", "env-val"); + basicTest("date", "1", Date.now()); + basicTest("date", "iso", (new Date()).toISOString()); + // basicTest("date", "object", Date.now()); it('should output 1 then 0 when triggered (default)', function(done) { var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", duration:"20", wires:[["n2"]] }, From 892933ff758d240109fb0d711d322c73f672250d Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Sat, 12 Oct 2024 17:22:24 +0100 Subject: [PATCH 044/121] Update 89-trigger_spec.js --- test/nodes/core/function/89-trigger_spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/nodes/core/function/89-trigger_spec.js b/test/nodes/core/function/89-trigger_spec.js index e86ed7a31..f78db0423 100644 --- a/test/nodes/core/function/89-trigger_spec.js +++ b/test/nodes/core/function/89-trigger_spec.js @@ -112,7 +112,7 @@ describe('trigger node', function() { if (rval) { msg.should.have.property("payload"); if (type == "date" && val == "1") { - should.deepEqual(Math.round(msg.payload/10000), Math.round(rval/10000)); + should.deepEqual(Math.round(msg.payload/1000000), Math.round(Date.now()/1000000)); } else if (type == "date" && val == "iso") { should.deepEqual(msg.payload.substr(0,11), rval.substr(0,11)); @@ -152,7 +152,7 @@ describe('trigger node', function() { if (rval) { msg.should.have.property("payload"); if (type == "date" && val == "0") { - should.deepEqual(Math.round(msg.payload/10000), Math.round(rval/10000)); + should.deepEqual(Math.round(msg.payload/1000000), parseInt(Date.now()/1000000)); } else if (type == "date" && val == "iso") { should.deepEqual(msg.payload.substr(0,11), rval.substr(0,11)); From d0ad62a82bd2b3afea4183aa99fb2509589d7226 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 21 Oct 2024 16:24:21 +0100 Subject: [PATCH 045/121] Revert trigger node fix --- .../@node-red/nodes/core/function/89-trigger.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/function/89-trigger.js b/packages/node_modules/@node-red/nodes/core/function/89-trigger.js index 0fc6ad6fb..16a00e99d 100644 --- a/packages/node_modules/@node-red/nodes/core/function/89-trigger.js +++ b/packages/node_modules/@node-red/nodes/core/function/89-trigger.js @@ -166,13 +166,7 @@ module.exports = function(RED) { if (err) { reject(err); } else { - if (node.op1type === "date") { - let d = new Date(); - if (node.op1 == "1") { msg.payload = Date.now(); } - else if (node.op1 == "iso") { msg.payload = d.toISOString(); } - else { msg.payload = d; } - } - else { msg.payload = value; } + msg.payload = value; resolve(); } }); @@ -219,12 +213,7 @@ module.exports = function(RED) { } else { msg2.payload = node.topics[topic].m2; - if (node.op2type === "date") { - let d = new Date(); - if (node.op2 == "0") { msg2.payload = Date.now(); } - else if (node.op2 == "iso") { msg2.payload = d.toISOString(); } - else { msg2.payload = d; } - } + if (node.op2type === "date") { msg2.payload = Date.now(); } if (node.second === true) { msgInfo.send([null,msg2]); } else { msgInfo.send(msg2); } } From b3aff3a3e6847f03ef851d98e68c80eafe27573d Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 21 Oct 2024 16:26:03 +0100 Subject: [PATCH 046/121] Ensure trigger node properties work with evaluateNodeProperty --- .../@node-red/nodes/core/function/89-trigger.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/nodes/core/function/89-trigger.js b/packages/node_modules/@node-red/nodes/core/function/89-trigger.js index 16a00e99d..f0aaaf393 100644 --- a/packages/node_modules/@node-red/nodes/core/function/89-trigger.js +++ b/packages/node_modules/@node-red/nodes/core/function/89-trigger.js @@ -24,6 +24,14 @@ module.exports = function(RED) { this.op2 = n.op2 || "0"; this.op1type = n.op1type || "str"; this.op2type = n.op2type || "str"; + // If the op1/2type is 'date', then we need to leave op1/2 alone so that + // evaluateNodeProperty works as expected. + if (this.op1type === 'date' && this.op1 === '1') { + this.op1 = '' + } + if (this.op2type === 'date' && this.op2 === '0') { + this.op2 = '' + } this.second = (n.outputs == 2) ? true : false; this.topic = n.topic || "topic"; @@ -213,7 +221,6 @@ module.exports = function(RED) { } else { msg2.payload = node.topics[topic].m2; - if (node.op2type === "date") { msg2.payload = Date.now(); } if (node.second === true) { msgInfo.send([null,msg2]); } else { msgInfo.send(msg2); } } From 2220956007347017a37d273ff8e1389f3bd48158 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 21 Oct 2024 16:35:11 +0100 Subject: [PATCH 047/121] Ensure trigger second output is revaluated for date types --- .../node_modules/@node-red/nodes/core/function/89-trigger.js | 2 +- test/nodes/core/function/89-trigger_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/function/89-trigger.js b/packages/node_modules/@node-red/nodes/core/function/89-trigger.js index f0aaaf393..8b3625f2d 100644 --- a/packages/node_modules/@node-red/nodes/core/function/89-trigger.js +++ b/packages/node_modules/@node-red/nodes/core/function/89-trigger.js @@ -201,7 +201,7 @@ module.exports = function(RED) { if (node.op2type !== "nul") { var promise = Promise.resolve(); msg2 = RED.util.cloneMessage(msg); - if (node.op2type === "flow" || node.op2type === "global") { + if (node.op2type === "flow" || node.op2type === "global" || node.op2type === "date") { promise = new Promise((resolve,reject) => { RED.util.evaluateNodeProperty(node.op2,node.op2type,node,msg,(err,value) => { if (err) { diff --git a/test/nodes/core/function/89-trigger_spec.js b/test/nodes/core/function/89-trigger_spec.js index f78db0423..913298901 100644 --- a/test/nodes/core/function/89-trigger_spec.js +++ b/test/nodes/core/function/89-trigger_spec.js @@ -152,7 +152,7 @@ describe('trigger node', function() { if (rval) { msg.should.have.property("payload"); if (type == "date" && val == "0") { - should.deepEqual(Math.round(msg.payload/1000000), parseInt(Date.now()/1000000)); + ;(Math.round(msg.payload/1000000)).should.be.approximately(parseInt(Date.now()/1000000), 1); } else if (type == "date" && val == "iso") { should.deepEqual(msg.payload.substr(0,11), rval.substr(0,11)); From 7555e0644f3bc78cfa87f930ecbe9f7e99d3cb6c Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Wed, 23 Oct 2024 12:15:24 +0200 Subject: [PATCH 048/121] Apply `envVarExcludes` setting to `RED.util.getSetting` into the function node --- .../nodes/core/function/10-function.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.js b/packages/node_modules/@node-red/nodes/core/function/10-function.js index 0120d8c92..8d365722f 100644 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.js +++ b/packages/node_modules/@node-red/nodes/core/function/10-function.js @@ -111,8 +111,6 @@ module.exports = function(RED) { throw new Error(RED._("function.error.externalModuleNotAllowed")); } - - var functionText = "var results = null;"+ "results = (async function(msg,__send__,__done__){ "+ "var __msgid__ = msg._msgid;"+ @@ -160,13 +158,26 @@ module.exports = function(RED) { node.outstandingIntervals = []; node.clearStatus = false; + const envVarExcludes = {}; + if (RED.settings.envVarExcludes && Array.isArray(RED.settings.envVarExcludes)) { + RED.settings.envVarExcludes.forEach((e) => envVarExcludes[e] = true); + } + var sandbox = { console:console, util:util, Buffer:Buffer, Date: Date, RED: { - util: RED.util + util: { + ...RED.util, + getSetting: function (node, name, flow) { + if (envVarExcludes[name]) { + return undefined; + } + return RED.util.getSetting(node, name, flow); + } + } }, __node__: { id: node.id, From 2fd7aee4dad20b8eea860f684dd80538e29e6892 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Thu, 24 Oct 2024 13:44:40 +0200 Subject: [PATCH 049/121] Move `envVarExcludes` to the top scope --- .../@node-red/nodes/core/function/10-function.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.js b/packages/node_modules/@node-red/nodes/core/function/10-function.js index 8d365722f..413830b72 100644 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.js +++ b/packages/node_modules/@node-red/nodes/core/function/10-function.js @@ -22,6 +22,11 @@ module.exports = function(RED) { var acorn = require("acorn"); var acornWalk = require("acorn-walk"); + const envVarExcludes = {}; + if (RED.settings.envVarExcludes && Array.isArray(RED.settings.envVarExcludes)) { + RED.settings.envVarExcludes.forEach((e) => envVarExcludes[e] = true); + } + function sendResults(node,send,_msgid,msgs,cloneFirstMessage) { if (msgs == null) { return; @@ -158,11 +163,6 @@ module.exports = function(RED) { node.outstandingIntervals = []; node.clearStatus = false; - const envVarExcludes = {}; - if (RED.settings.envVarExcludes && Array.isArray(RED.settings.envVarExcludes)) { - RED.settings.envVarExcludes.forEach((e) => envVarExcludes[e] = true); - } - var sandbox = { console:console, util:util, From 83696abf9db7f433c38dec5f9deb5092f696bb71 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Fri, 25 Oct 2024 17:34:51 +0200 Subject: [PATCH 050/121] Fixes and improvements with comments --- .../src/js/ui/editors/panes/properties.js | 49 ++++++++++++------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js index 9b18ec35e..8f78b0a9a 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js @@ -131,12 +131,15 @@ } } if (node._def.credentials) { - const credsDefinition = node._def.credentials; - const credsChanged = updateNodeCredentials(node, credsDefinition, this.inputClass); + const credDefinition = node._def.credentials; + const credChanges = updateNodeCredentials(node, credDefinition, this.inputClass); - if (credsChanged) { + if (Object.keys(credChanges).length) { editState.changed = true; - editState.changes.credentials = node.credentials._; + editState.changes.credentials = { + ...(editState.changes.credentials || {}), + ...credChanges + }; } } } @@ -165,10 +168,11 @@ * @param node - the node containing the credentials * @param credDefinition - definition of the credentials * @param prefix - prefix of the input fields - * @return {boolean} whether anything has changed + * @return {object} an object containing the modified properties */ function updateNodeCredentials(node, credDefinition, prefix) { - var changed = false; + const changes = {}; + if (!node.credentials) { node.credentials = {_:{}}; } else if (!node.credentials._) { @@ -181,23 +185,32 @@ if (input.length > 0) { var value = input.val(); if (credDefinition[cred].type == 'password') { - node.credentials['has_' + cred] = (value !== ""); - - // Skip if the credential has not changed - if ((value === '__PWRD__' && node.credentials._['has_' + cred] === true) || - (value === "" && node.credentials._['has_' + cred] === false)) { - continue; + if (value === '__PWRD__') { + // A cred value exists - no changes + } else if (value === '' && node.credentials['has_' + cred] === false) { + // Empty cred value exists - no changes + } else if (value === node.credentials[cred]) { + // A cred value exists locally in the editor - no changes + // Like the user sets a value, saves the config, + // reopens the config and save the config again + } else { + changes[cred] = node.credentials[cred]; + node.credentials[cred] = value; } - changed = true; - } - node.credentials[cred] = value; - if (value != node.credentials._[cred]) { - changed = true; + node.credentials['has_' + cred] = (value !== ''); + } else { + // Since these creds are loaded by the editor, + // values can be directly compared + if (value !== node.credentials[cred]) { + changes[cred] = node.credentials[cred]; + node.credentials[cred] = value; + } } } } } - return changed; + + return changes; } })(); From 966064328f6fd92145729f7710da82cd98c3997d Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Fri, 25 Oct 2024 23:33:19 +0200 Subject: [PATCH 051/121] Add `oneditsave` credentials changes to history --- .../editor-client/src/js/ui/editor.js | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index 1bdd134fb..b656af0b0 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -804,6 +804,17 @@ RED.editor = (function() { } } + const oldCreds = {}; + if (editing_node._def.credentials) { + for (const prop in editing_node._def.credentials) { + if (Object.prototype.hasOwnProperty.call(editing_node._def.credentials, prop)) { + if (prop in editing_node.credentials) { + oldCreds[prop] = editing_node.credentials[prop]; + } + } + } + } + try { const rc = editing_node._def.oneditsave.call(editing_node); if (rc === true) { @@ -835,6 +846,21 @@ RED.editor = (function() { } } } + + if (editing_node._def.credentials) { + for (const prop in editing_node._def.credentials) { + if (Object.prototype.hasOwnProperty.call(editing_node._def.credentials, prop)) { + if (oldCreds[prop] !== editing_node.credentials[prop]) { + if (editing_node.credentials[prop] === '__PWRD__') { + continue; + } + editState.changes.credentials = editState.changes.credentials || {}; + editState.changes.credentials[prop] = oldCreds[prop]; + editState.changed = true; + } + } + } + } } } From 70aed23ef0a0792d4044a93ec043438dbc586c22 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:31:36 +0100 Subject: [PATCH 052/121] Fix `envVarList` sortable value --- .../@node-red/editor-client/src/js/ui/editors/envVarList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/envVarList.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/envVarList.js index dda5d1660..b974fbc50 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/envVarList.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/envVarList.js @@ -131,7 +131,7 @@ RED.editor.envVarList = (function() { nameField.trigger('change'); } }, - sortable: ".red-ui-editableList-item-handle", + sortable: true, removable: false }); var parentEnv = {}; From f67268b89ad654fb57b2f10e02ea2ebc7b6b7da7 Mon Sep 17 00:00:00 2001 From: Gauthier Dandele <92022724+GogoVega@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:01:42 +0100 Subject: [PATCH 053/121] Revert and force getSetting to use the local node Co-authored-by: Nick O'Leary --- .../@node-red/nodes/core/function/10-function.js | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.js b/packages/node_modules/@node-red/nodes/core/function/10-function.js index 413830b72..71f6e15d6 100644 --- a/packages/node_modules/@node-red/nodes/core/function/10-function.js +++ b/packages/node_modules/@node-red/nodes/core/function/10-function.js @@ -22,11 +22,6 @@ module.exports = function(RED) { var acorn = require("acorn"); var acornWalk = require("acorn-walk"); - const envVarExcludes = {}; - if (RED.settings.envVarExcludes && Array.isArray(RED.settings.envVarExcludes)) { - RED.settings.envVarExcludes.forEach((e) => envVarExcludes[e] = true); - } - function sendResults(node,send,_msgid,msgs,cloneFirstMessage) { if (msgs == null) { return; @@ -171,11 +166,9 @@ module.exports = function(RED) { RED: { util: { ...RED.util, - getSetting: function (node, name, flow) { - if (envVarExcludes[name]) { - return undefined; - } - return RED.util.getSetting(node, name, flow); + getSetting: function (_node, name, _flow) { + // Ensure `node` argument is the Function node and do not allow flow to be overridden. + return RED.util.getSetting(node, name); } } }, From a7ee31307e20a1412b928cf8d2e4b7edc3320afe Mon Sep 17 00:00:00 2001 From: Ersin Date: Tue, 29 Oct 2024 13:38:32 +0100 Subject: [PATCH 054/121] missing getSubscriptions in the docs while its implemented See: https://github.com/node-red/node-red/blob/master/packages/node_modules/%40node-red/nodes/core/network/10-mqtt.js#L1288 --- .../@node-red/nodes/locales/en-US/network/10-mqtt.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/network/10-mqtt.html b/packages/node_modules/@node-red/nodes/locales/en-US/network/10-mqtt.html index f4b0012ce..68f89ae4a 100644 --- a/packages/node_modules/@node-red/nodes/locales/en-US/network/10-mqtt.html +++ b/packages/node_modules/@node-red/nodes/locales/en-US/network/10-mqtt.html @@ -48,7 +48,8 @@
action string
the name of the action the node should perform. Available actions are: "connect", - "disconnect", "subscribe" and "unsubscribe".
+ "disconnect", "getSubscriptions", "subscribe" and + "unsubscribe".
topic string|object|array
For the "subscribe" and "unsubscribe" actions, this property provides the topic. It can be set as either:
    From 443492d6eb27be98e5a01176384cf3eea7b99645 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:12:13 +0100 Subject: [PATCH 055/121] Fix `setModulePendingUpdated` with plugins --- .../node_modules/@node-red/editor-client/src/js/nodes.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 12e0a6ef1..bcd1f8c5a 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -73,7 +73,13 @@ RED.nodes = (function() { var exports = { setModulePendingUpdated: function(module,version) { - moduleList[module].pending_version = version; + if (!!RED.plugins.getModule(module)) { + // The module updated is a plugin + RED.plugins.getModule(module).pending_version = version; + } else { + moduleList[module].pending_version = version; + } + RED.events.emit("registry:module-updated",{module:module,version:version}); }, getModule: function(module) { From 33a5b2527c929a6b06ed8d6a7da17bed297b5117 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Thu, 31 Oct 2024 17:06:13 +0000 Subject: [PATCH 056/121] Make delay node rate limit reset consistent - not send on reset. to fix #4830 --- .../@node-red/nodes/core/function/89-delay.js | 11 +++++---- test/nodes/core/function/89-delay_spec.js | 23 +++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/function/89-delay.js b/packages/node_modules/@node-red/nodes/core/function/89-delay.js index b0fc2ed86..caf0bd892 100644 --- a/packages/node_modules/@node-red/nodes/core/function/89-delay.js +++ b/packages/node_modules/@node-red/nodes/core/function/89-delay.js @@ -253,9 +253,11 @@ module.exports = function(RED) { if (node.allowrate && m.hasOwnProperty("rate") && !isNaN(parseFloat(m.rate))) { node.rate = m.rate; } - send(m); - node.reportDepth(); - node.intervalID = setInterval(sendMsgFromBuffer, node.rate); + if (!msg.hasOwnProperty("reset")) { + send(m); + node.reportDepth(); + node.intervalID = setInterval(sendMsgFromBuffer, node.rate); + } done(); } } @@ -303,7 +305,8 @@ module.exports = function(RED) { node.droppedMsgs++; } } - } else { + } + else { if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate))) { node.rate = msg.rate; } diff --git a/test/nodes/core/function/89-delay_spec.js b/test/nodes/core/function/89-delay_spec.js index 46b0037bc..4fbf3df54 100644 --- a/test/nodes/core/function/89-delay_spec.js +++ b/test/nodes/core/function/89-delay_spec.js @@ -1009,6 +1009,29 @@ describe('delay Node', function() { }); }); + it('sending a msg with reset to empty queue doesnt send anything', function(done) { + this.timeout(2000); + var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":1,"timeoutUnits":"seconds","rate":2,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, + {id:"helperNode1", type:"helper", wires:[]}]; + helper.load(delayNode, flow, function() { + var delayNode1 = helper.getNode("delayNode1"); + var helperNode1 = helper.getNode("helperNode1"); + var t = Date.now(); + var c = 0; + helperNode1.on("input", function(msg) { + console.log("Shold not get here") + done(e); + }); + + setTimeout( function() { + if (c === 0) { done(); } + }, 250); + + // send test messages + delayNode1.receive({payload:1,topic:"foo",reset:true}); // send something with blank topic + }); + }); + /* Messaging API support */ function mapiDoneTestHelper(done, pauseType, drop, msgAndTimings) { const completeNode = require("nr-test-utils").require("@node-red/nodes/core/common/24-complete.js"); From d3219f0600e11ae17577f2579514d0c212e47208 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Thu, 31 Oct 2024 17:21:53 +0000 Subject: [PATCH 057/121] do add to queue in case it needs to also be flushed --- .../@node-red/nodes/core/function/89-delay.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/function/89-delay.js b/packages/node_modules/@node-red/nodes/core/function/89-delay.js index caf0bd892..8f9964115 100644 --- a/packages/node_modules/@node-red/nodes/core/function/89-delay.js +++ b/packages/node_modules/@node-red/nodes/core/function/89-delay.js @@ -253,11 +253,15 @@ module.exports = function(RED) { if (node.allowrate && m.hasOwnProperty("rate") && !isNaN(parseFloat(m.rate))) { node.rate = m.rate; } - if (!msg.hasOwnProperty("reset")) { - send(m); - node.reportDepth(); - node.intervalID = setInterval(sendMsgFromBuffer, node.rate); + if (msg.hasOwnProperty("reset")) { + if (msg.hasOwnProperty("flush")) { + node.buffer.push({msg: m, send: send, done: done}); + } } + else { send(m); } + + node.reportDepth(); + node.intervalID = setInterval(sendMsgFromBuffer, node.rate); done(); } } From abe0b60bf7b9f765ef172fe1ecf3b695f12d32a4 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Wed, 6 Nov 2024 17:28:51 +0100 Subject: [PATCH 058/121] Remove disabled node types from QuickAddDialog list --- .../@node-red/editor-client/src/js/ui/typeSearch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js b/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js index 216cc3e40..9a83409da 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js @@ -382,7 +382,7 @@ RED.typeSearch = (function() { var items = []; RED.nodes.registry.getNodeTypes().forEach(function(t) { var def = RED.nodes.getType(t); - if (def.category !== 'config' && t !== 'unknown' && t !== 'tab') { + if (def.set.enabled && def.category !== 'config' && t !== 'unknown' && t !== 'tab') { items.push({type:t,def: def, label:getTypeLabel(t,def)}); } }); From 3d9bc265dde7a21f0e6aad83b89af967b10302cc Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Thu, 7 Nov 2024 21:58:11 +0100 Subject: [PATCH 059/121] Handle users of env config nodes when adding/removing subflow node --- .../@node-red/editor-client/src/js/nodes.js | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 12e0a6ef1..ace560f4a 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -809,6 +809,20 @@ RED.nodes = (function() { if (sf) { sf.instances.splice(sf.instances.indexOf(node),1); } + + node.env?.forEach((prop) => { + if (prop.type === "conf-type" && prop.value) { + // Remove the node from the config node users + const configNode = getNode(prop.value); + if (configNode) { + if (configNode.users.indexOf(node) !== -1) { + configNode.users.splice(configNode.users.indexOf(node), 1); + RED.events.emit('nodes:change', configNode); + updatedConfigNode = true; + } + } + } + }); } if (updatedConfigNode) { @@ -2684,6 +2698,22 @@ RED.nodes = (function() { } } } + + // Subflows can have config node env + if (n.type.indexOf("subflow:") === 0) { + n.env?.forEach((prop) => { + if (prop.type === "conf-type" && prop.value) { + // Add the node to the config node users + const configNode = getNode(prop.value); + if (configNode) { + if (configNode.users.indexOf(n) === -1) { + configNode.users.push(n); + RED.events.emit('nodes:change', configNode); + } + } + } + }); + } } function flowVersion(version) { From f2d72b1050874f0d049d1bafef015bf13fd82d94 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Thu, 7 Nov 2024 21:59:14 +0100 Subject: [PATCH 060/121] Handle users of env config nodes when saving subflow node config --- .../js/ui/editors/panes/envVarProperties.js | 31 ++++++++++++++++++- .../src/js/ui/editors/panes/properties.js | 1 + .../editor-client/src/js/ui/subflow.js | 2 +- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/envVarProperties.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/envVarProperties.js index b004662be..0c7694126 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/envVarProperties.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/envVarProperties.js @@ -20,10 +20,31 @@ apply: function(editState) { var old_env = node.env; var new_env = []; + if (/^subflow:/.test(node.type)) { + // Get the list of environment variables from the node properties new_env = RED.subflow.exportSubflowInstanceEnv(node); } + if (old_env && old_env.length) { + old_env.forEach(function (prop) { + if (prop.type === "conf-type" && prop.value) { + const stillInUse = new_env?.some((p) => p.type === "conf-type" && p.name === prop.name && p.value === prop.value); + if (!stillInUse) { + // Remove the node from the config node users + // Only for empty value or modified + const configNode = RED.nodes.node(prop.value); + if (configNode) { + if (configNode.users.indexOf(node) !== -1) { + configNode.users.splice(configNode.users.indexOf(node), 1); + RED.events.emit('nodes:change', configNode) + } + } + } + } + }); + } + // Get the values from the Properties table tab var items = this.list.editableList('items'); items.each(function (i,el) { @@ -41,7 +62,6 @@ } }); - if (new_env && new_env.length > 0) { new_env.forEach(function(prop) { if (prop.type === "cred") { @@ -52,6 +72,15 @@ editState.changed = true; } delete prop.value; + } else if (prop.type === "conf-type" && prop.value) { + const configNode = RED.nodes.node(prop.value); + if (configNode) { + if (configNode.users.indexOf(node) === -1) { + // Add the node to the config node users + configNode.users.push(node); + RED.events.emit('nodes:change', configNode); + } + } } }); } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js index cfa72be10..3f35d98a8 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js @@ -44,6 +44,7 @@ apply: function(editState) { var newValue; var d; + // If the node is a subflow, the node's properties (exepts name) are saved by `envProperties` if (node._def.defaults) { for (d in node._def.defaults) { if (node._def.defaults.hasOwnProperty(d)) { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js index 751bf9cfb..3e1b9a410 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js @@ -1362,7 +1362,7 @@ RED.subflow = (function() { item.value = ""+input.prop("checked"); break; case "conf-types": - item.value = input.val() + item.value = input.val() === "_ADD_" ? "" : input.val(); item.type = "conf-type" } if (ui.type === "cred" || item.type !== data.parent.type || item.value !== data.parent.value) { From deccfdf654982fcb2127cb061978d2d36939a2fd Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Thu, 7 Nov 2024 22:00:36 +0100 Subject: [PATCH 061/121] Handle users of env config nodes when undo subflow node changes --- .../@node-red/editor-client/src/js/history.js | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/node_modules/@node-red/editor-client/src/js/history.js b/packages/node_modules/@node-red/editor-client/src/js/history.js index 2fa4e4427..60e71c391 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/history.js +++ b/packages/node_modules/@node-red/editor-client/src/js/history.js @@ -453,10 +453,48 @@ RED.history = (function() { RED.events.emit("nodes:change",newConfigNode); } }); + } else if (i === "env" && ev.node.type.indexOf("subflow:") === 0) { + // Subflow can have config node in node.env + let nodeList = ev.node.env || []; + nodeList = nodeList.reduce((list, prop) => { + if (prop.type === "conf-type" && prop.value) { + list.push(prop.value); + } + return list; + }, []); + + nodeList.forEach(function(id) { + const configNode = RED.nodes.node(id); + if (configNode) { + if (configNode.users.indexOf(ev.node) !== -1) { + configNode.users.splice(configNode.users.indexOf(ev.node), 1); + RED.events.emit("nodes:change", configNode); + } + } + }); + + nodeList = ev.changes.env || []; + nodeList = nodeList.reduce((list, prop) => { + if (prop.type === "conf-type" && prop.value) { + list.push(prop.value); + } + return list; + }, []); + + nodeList.forEach(function(id) { + const configNode = RED.nodes.node(id); + if (configNode) { + if (configNode.users.indexOf(ev.node) === -1) { + configNode.users.push(ev.node); + RED.events.emit("nodes:change", configNode); + } + } + }); } ev.node[i] = ev.changes[i]; } } + ev.node.dirty = true; ev.node.changed = ev.changed; From c8a02d53e89efe39605f859f29acc96f1884b1fe Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Thu, 7 Nov 2024 22:06:45 +0100 Subject: [PATCH 062/121] Ensure the node added to config node users is the proxy object --- packages/node_modules/@node-red/editor-client/src/js/nodes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index ace560f4a..b62f54b27 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -702,11 +702,11 @@ RED.nodes = (function() { n["_"] = RED._; } if (n._def.category == "config") { - configNodes[n.id] = n; + configNodes[n.id] = newNode; } else { if (n.wires && (n.wires.length > n.outputs)) { n.outputs = n.wires.length; } n.dirty = true; - updateConfigNodeUsers(n); + updateConfigNodeUsers(newNode); if (n._def.category == "subflows" && typeof n.i === "undefined") { var nextId = 0; RED.nodes.eachNode(function(node) { From 59a133cc13e5e0fc3b7bc8b84272377b9bd820bd Mon Sep 17 00:00:00 2001 From: Gauthier Dandele <92022724+GogoVega@users.noreply.github.com> Date: Fri, 8 Nov 2024 12:28:40 +0100 Subject: [PATCH 063/121] Need to guard against subflows that doesn't have a set property Co-authored-by: Nick O'Leary --- .../@node-red/editor-client/src/js/ui/typeSearch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js b/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js index 9a83409da..f284f2464 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js @@ -382,7 +382,7 @@ RED.typeSearch = (function() { var items = []; RED.nodes.registry.getNodeTypes().forEach(function(t) { var def = RED.nodes.getType(t); - if (def.set.enabled && def.category !== 'config' && t !== 'unknown' && t !== 'tab') { + if (def.set?.enabled !== false && def.category !== 'config' && t !== 'unknown' && t !== 'tab') { items.push({type:t,def: def, label:getTypeLabel(t,def)}); } }); From 046d56d692bde24a1f3d12ea8a9dcc9c2e5c420a Mon Sep 17 00:00:00 2001 From: Ahmed Ghorab Date: Sun, 10 Nov 2024 13:30:41 +0200 Subject: [PATCH 064/121] Add qoutes when installing local tgz to fix spacing in the file path --- .../@node-red/registry/lib/installer.js | 2 +- .../@node-red/registry/lib/installer_spec.js | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/registry/lib/installer.js b/packages/node_modules/@node-red/registry/lib/installer.js index 495a956af..3ffd65513 100644 --- a/packages/node_modules/@node-red/registry/lib/installer.js +++ b/packages/node_modules/@node-red/registry/lib/installer.js @@ -144,7 +144,7 @@ async function installModule(module,version,url) { if (url) { if (pkgurlRe.test(url) || localtgzRe.test(url)) { // Git remote url or Tarball url - check the valid package url - installName = url; + installName = localtgzRe.test(url) && slashRe.test(url) ? `"${url}"` : url; isRegistryPackage = false; } else { log.warn(log._("server.install.install-failed-url",{name:module,url:url})); diff --git a/test/unit/@node-red/registry/lib/installer_spec.js b/test/unit/@node-red/registry/lib/installer_spec.js index 7f514e99d..f7ca223e8 100644 --- a/test/unit/@node-red/registry/lib/installer_spec.js +++ b/test/unit/@node-red/registry/lib/installer_spec.js @@ -258,6 +258,29 @@ describe('nodes/registry/installer', function() { }).catch(done); }); + it("succeeds when file path is valid node-red module", function(done) { + var nodeInfo = {nodes:{module:"foo",types:["a"]}}; + + var res = { + code: 0, + stdout:"", + stderr:"" + } + var p = Promise.resolve(res); + p.catch((err)=>{}); + execResponse = p; + + var addModule = sinon.stub(registry,"addModule").callsFake(function(md) { + return Promise.resolve(nodeInfo); + }); + + installer.installModule("foo",null,"/example path/foo-0.1.1.tgz").then(function(info) { + exec.run.lastCall.args[1].should.eql([ 'install', '--no-audit', '--no-update-notifier', '--no-fund', '--save', '--save-prefix=~', '--omit=dev', '--engine-strict', '"/example path/foo-0.1.1.tgz"' ]); + info.should.eql(nodeInfo); + done(); + }).catch(done); + }); + it("triggers preInstall and postInstall hooks", function(done) { let receivedPreEvent,receivedPostEvent; hooks.add("preInstall", function(event) { event.args = ["a"]; receivedPreEvent = event; }) From ad615a76c8bb44686d5f481cefcb271184ef4c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B8=BF=E5=88=99?= Date: Thu, 14 Nov 2024 16:31:36 +0800 Subject: [PATCH 065/121] Change groups.length to groups.size Fix wrong length attribute of Set --- packages/node_modules/@node-red/editor-client/src/js/ui/view.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index ff0091e65..d59c651d6 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -288,7 +288,7 @@ RED.view = (function() { } selectedLinks.clearUnselected() }, - length: () => groups.length, + length: () => groups.size, forEach: (func) => { groups.forEach(func) }, toArray: () => [...groups], clear: function () { From 6d6e6fa416af2196c393b3bdb3bcb78c30616942 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:30:47 +0100 Subject: [PATCH 066/121] Get the env config node from the parent subflow --- packages/node_modules/@node-red/runtime/lib/flows/util.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/node_modules/@node-red/runtime/lib/flows/util.js b/packages/node_modules/@node-red/runtime/lib/flows/util.js index fa25a26d0..6b7f659b9 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/util.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/util.js @@ -113,6 +113,10 @@ async function evaluateEnvProperties(flow, env, credentials) { resolve() }); })) + } else if (type === "conf-type" && /^\${[^}]+}$/.test(value)) { + // Get the config node from the parent subflow + const name = value.substring(2, value.length - 1); + value = flow.getSetting(name); } else { try { value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null); From 4cb3ccc9844f1852effdde1aa104ec2232aba41a Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 18 Nov 2024 16:20:58 +0000 Subject: [PATCH 067/121] Rename variable to avoid confusion in view.js --- .../@node-red/editor-client/src/js/ui/view.js | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index d59c651d6..245b6db75 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -2689,22 +2689,21 @@ RED.view = (function() { addToRemovedLinks(reconnectResult.removedLinks) } - var startDirty = RED.nodes.dirty(); - var startChanged = false; - var selectedGroups = []; + const startDirty = RED.nodes.dirty(); + let movingSelectedGroups = []; if (movingSet.length() > 0) { for (var i=0;i=0; i--) { - var g = selectedGroups[i]; + for (i = movingSelectedGroups.length-1; i>=0; i--) { + var g = movingSelectedGroups[i]; removedGroups.push(g); RED.nodes.removeGroup(g); } From 94e3fdd7a9dab67264a46350f8c67b30dc137612 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 18 Nov 2024 17:12:28 +0000 Subject: [PATCH 068/121] Validate json dropped into editor to avoid unhelpful error messages Fixes #4962 --- .../editor-client/src/js/ui/clipboard.js | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js b/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js index 4e16bd3f6..435e4a8cd 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js @@ -334,6 +334,30 @@ RED.clipboard = (function() { },100); } + /** + * Validates if the provided string looks like valid flow json + * @param {string} flowString the string to validate + * @returns If valid, returns the node array + */ + function validateFlowString(flowString) { + const res = JSON.parse(flowString) + if (!Array.isArray(res)) { + throw new Error(RED._("clipboard.import.errors.notArray")); + } + for (let i = 0; i < res.length; i++) { + if (typeof res[i] !== "object") { + throw new Error(RED._("clipboard.import.errors.itemNotObject",{index:i})); + } + if (!Object.hasOwn(res[i], 'id')) { + throw new Error(RED._("clipboard.import.errors.missingId",{index:i})); + } + if (!Object.hasOwn(res[i], 'type')) { + throw new Error(RED._("clipboard.import.errors.missingType",{index:i})); + } + } + return res + } + var validateImportTimeout; function validateImport() { if (activeTab === "red-ui-clipboard-dialog-import-tab-clipboard") { @@ -351,21 +375,7 @@ RED.clipboard = (function() { return; } try { - if (!/^\[[\s\S]*\]$/m.test(v)) { - throw new Error(RED._("clipboard.import.errors.notArray")); - } - var res = JSON.parse(v); - for (var i=0;i Date: Mon, 25 Nov 2024 21:07:42 +0100 Subject: [PATCH 069/121] Add a guard to check if wires exist --- .../@node-red/editor-client/src/js/nodes.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index f7b61f3a6..e3df86f6c 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -2336,12 +2336,14 @@ RED.nodes = (function() { // The node declares outputs in its defaults, but has not got a valid value // Defer to the length of the wires array - if (isNaN(node.outputs)) { - node.outputs = node.wires.length; - } else if (node.wires.length > node.outputs) { - // If 'wires' is longer than outputs, clip wires - console.log("Warning: node.wires longer than node.outputs - trimming wires:", node.id, " wires:", node.wires.length, " outputs:", node.outputs); - node.wires = node.wires.slice(0, node.outputs); + if (node.hasOwnProperty('wires')) + if (isNaN(node.outputs)) { + node.outputs = node.wires.length; + } else if (node.wires.length > node.outputs) { + // If 'wires' is longer than outputs, clip wires + console.log("Warning: node.wires longer than node.outputs - trimming wires:", node.id, " wires:", node.wires.length, " outputs:", node.outputs); + node.wires = node.wires.slice(0, node.outputs); + } } for (d in node._def.defaults) { From f6cf051282301b1e06aa77709deb1f874197f3d9 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 2 Dec 2024 17:03:14 +0000 Subject: [PATCH 070/121] Fix junction insert position via context menu --- .../editor-client/src/js/ui/contextMenu.js | 14 ++++--- .../editor-client/src/js/ui/view-tools.js | 41 +++++++++++-------- .../@node-red/editor-client/src/js/ui/view.js | 4 +- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js b/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js index 698842dbd..53ebe5c4b 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js @@ -54,15 +54,15 @@ RED.contextMenu = (function () { } } + const scale = RED.view.scale() const offset = $("#red-ui-workspace-chart").offset() - - let addX = options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft() - let addY = options.y - offset.top + $("#red-ui-workspace-chart").scrollTop() + let addX = (options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft()) / scale + let addY = (options.y - offset.top + $("#red-ui-workspace-chart").scrollTop()) / scale if (RED.view.snapGrid) { const gridSize = RED.view.gridSize() - addX = gridSize * Math.floor(addX / gridSize) - addY = gridSize * Math.floor(addY / gridSize) + addX = gridSize * Math.round(addX / gridSize) + addY = gridSize * Math.round(addY / gridSize) } if (RED.settings.theme("menu.menu-item-action-list", true)) { @@ -87,7 +87,9 @@ RED.contextMenu = (function () { }, (hasLinks) ? { // has least 1 wire selected label: RED._("contextMenu.junction"), - onselect: 'core:split-wires-with-junctions', + onselect: function () { + RED.actions.invoke('core:split-wires-with-junctions', { x: addX, y: addY }) + }, disabled: !canEdit || !hasLinks } : { label: RED._("contextMenu.junction"), diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js index 22cb1eecd..eecd309d1 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js @@ -1154,11 +1154,11 @@ RED.view.tools = (function() { } } - function addJunctionsToWires(wires) { + function addJunctionsToWires(options = {}) { if (RED.workspaces.isLocked()) { return } - let wiresToSplit = wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link)); + let wiresToSplit = options.wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link)); if (!wiresToSplit) { return } @@ -1206,21 +1206,26 @@ RED.view.tools = (function() { if (links.length === 0) { return } - let pointCount = 0 - links.forEach(function(l) { - if (l._sliceLocation) { - junction.x += l._sliceLocation.x - junction.y += l._sliceLocation.y - delete l._sliceLocation - pointCount++ - } else { - junction.x += l.source.x + l.source.w/2 + l.target.x - l.target.w/2 - junction.y += l.source.y + l.target.y - pointCount += 2 - } - }) - junction.x = Math.round(junction.x/pointCount) - junction.y = Math.round(junction.y/pointCount) + if (addedJunctions.length === 0 && Object.hasOwn(options, 'x') && Object.hasOwn(options, 'y')) { + junction.x = options.x + junction.y = options.y + } else { + let pointCount = 0 + links.forEach(function(l) { + if (l._sliceLocation) { + junction.x += l._sliceLocation.x + junction.y += l._sliceLocation.y + delete l._sliceLocation + pointCount++ + } else { + junction.x += l.source.x + l.source.w/2 + l.target.x - l.target.w/2 + junction.y += l.source.y + l.target.y + pointCount += 2 + } + }) + junction.x = Math.round(junction.x/pointCount) + junction.y = Math.round(junction.y/pointCount) + } if (RED.view.snapGrid) { let gridSize = RED.view.gridSize() junction.x = (gridSize*Math.round(junction.x/gridSize)); @@ -1410,7 +1415,7 @@ RED.view.tools = (function() { RED.actions.add("core:wire-multiple-to-node", function() { wireMultipleToNode() }) RED.actions.add("core:split-wire-with-link-nodes", function () { splitWiresWithLinkNodes() }); - RED.actions.add("core:split-wires-with-junctions", function () { addJunctionsToWires() }); + RED.actions.add("core:split-wires-with-junctions", function (options) { addJunctionsToWires(options) }); RED.actions.add("core:generate-node-names", generateNodeNames ) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index 245b6db75..61ee3e3f6 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -321,8 +321,8 @@ RED.view = (function() { evt.stopPropagation() RED.contextMenu.show({ type: 'workspace', - x:evt.clientX-5, - y:evt.clientY-5 + x: evt.clientX, + y: evt.clientY }) return false }) From e8d81d814cfb4317a4e663f47ffa19f5c4fa2eac Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 3 Dec 2024 10:15:59 +0000 Subject: [PATCH 071/121] Apply scaleFactor when calculating junction slice positions --- .../node_modules/@node-red/editor-client/src/js/ui/view.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index 61ee3e3f6..8ce6dc630 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -5174,8 +5174,8 @@ RED.view = (function() { var delta = Infinity; for (var i = 0; i < lineLength; i++) { var linePos = pathLine.getPointAtLength(i); - var posDeltaX = Math.abs(linePos.x-d3.event.offsetX) - var posDeltaY = Math.abs(linePos.y-d3.event.offsetY) + var posDeltaX = Math.abs(linePos.x-(d3.event.offsetX / scaleFactor)) + var posDeltaY = Math.abs(linePos.y-(d3.event.offsetY / scaleFactor)) var posDelta = posDeltaX*posDeltaX + posDeltaY*posDeltaY if (posDelta < delta) { pos = linePos From 0b09cf5fa9f1edc023587925b68708bef28c01a4 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 3 Dec 2024 14:51:32 +0000 Subject: [PATCH 072/121] Update packages/node_modules/@node-red/editor-client/src/js/nodes.js --- packages/node_modules/@node-red/editor-client/src/js/nodes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index e3df86f6c..a818d4a38 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -2336,7 +2336,7 @@ RED.nodes = (function() { // The node declares outputs in its defaults, but has not got a valid value // Defer to the length of the wires array - if (node.hasOwnProperty('wires')) + if (node.hasOwnProperty('wires')) { if (isNaN(node.outputs)) { node.outputs = node.wires.length; } else if (node.wires.length > node.outputs) { From aee531bf165f14ffa2df40f8f1a22b5185c3d3ed Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 3 Dec 2024 17:14:04 +0000 Subject: [PATCH 073/121] Update packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js Co-authored-by: Gauthier Dandele <92022724+GogoVega@users.noreply.github.com> --- .../@node-red/editor-client/src/js/ui/subflow.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js index fe5e81319..0bc5f11e1 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js @@ -508,7 +508,10 @@ RED.subflow = (function() { var activeSubflow = RED.nodes.subflow(id); RED.nodes.eachNode(function(n) { - if (n.z === id || (!keepInstanceNodes && n.type === "subflow:" + id)) { + if (!keepInstanceNodes && n.type == "subflow:"+id) { + removedNodes.push(n); + } + if (n.z == id) { RED.nodes.updateConfigNodeUsers(n, { action: "remove" }); removedNodes.push(n); } From 338ddf17de923421decdfa2d84756a80843b88cd Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 4 Dec 2024 09:55:09 +0000 Subject: [PATCH 074/121] Update packages/node_modules/@node-red/editor-client/src/js/nodes.js Co-authored-by: Gauthier Dandele <92022724+GogoVega@users.noreply.github.com> --- .../@node-red/editor-client/src/js/nodes.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 770400132..2359b9321 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -2722,14 +2722,14 @@ RED.nodes = (function() { * Update any config nodes referenced by the provided node to ensure * their 'users' list is correct. * - * Options: - * - `action` - Add or remove the node from the Config Node users list. Default `add`. - * - `emitEvent` - Emit the `nodes:changes` event. Default true. - * * @param {object} node The node in which to check if it contains references - * @param {{ action?: "add" | "remove"; emitEvent?: boolean; }} options Options to apply. + * @param {object} options Options to apply. + * @param {"add" | "remove"} [options.action] Add or remove the node from + * the Config Node users list. Default `add`. + * @param {boolean} [options.emitEvent] Emit the `nodes:changes` event. + * Default true. */ - function updateConfigNodeUsers(node, options = {}) { + function updateConfigNodeUsers(node, options) { const defaultOptions = { action: "add", emitEvent: true }; options = Object.assign({}, defaultOptions, options); From 92dff4bacd61f083ef80df012f6fbec034e62ca3 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 4 Dec 2024 10:41:10 +0000 Subject: [PATCH 075/121] Apply suggestions from code review Co-authored-by: Gauthier Dandele <92022724+GogoVega@users.noreply.github.com> --- .../@node-red/editor-client/src/js/nodes.js | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 2359b9321..366120796 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -812,20 +812,6 @@ RED.nodes = (function() { if (sf) { sf.instances.splice(sf.instances.indexOf(node),1); } - - node.env?.forEach((prop) => { - if (prop.type === "conf-type" && prop.value) { - // Remove the node from the config node users - const configNode = getNode(prop.value); - if (configNode) { - if (configNode.users.indexOf(node) !== -1) { - configNode.users.splice(configNode.users.indexOf(node), 1); - RED.events.emit('nodes:change', configNode); - updatedConfigNode = true; - } - } - } - }); } if (updatedConfigNode) { @@ -2764,15 +2750,27 @@ RED.nodes = (function() { } // Subflows can have config node env - if (n.type.indexOf("subflow:") === 0) { - n.env?.forEach((prop) => { + if (node.type.indexOf("subflow:") === 0) { + node.env?.forEach((prop) => { if (prop.type === "conf-type" && prop.value) { // Add the node to the config node users const configNode = getNode(prop.value); if (configNode) { - if (configNode.users.indexOf(n) === -1) { - configNode.users.push(n); - RED.events.emit('nodes:change', configNode); + if (options.action === "add") { + if (configNode.users.indexOf(node) === -1) { + configNode.users.push(node); + if (options.emitEvent) { + RED.events.emit('nodes:change', configNode); + } + } + } else if (options.action === "remove") { + if (configNode.users.indexOf(node) !== -1) { + const users = configNode.users; + users.splice(users.indexOf(node), 1); + if (options.emitEvent) { + RED.events.emit('nodes:change', configNode); + } + } } } } From f9877f8d0b6559729d3c3cd6bd2d8b9ee86e79f7 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 4 Dec 2024 10:41:27 +0000 Subject: [PATCH 076/121] Update packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js Co-authored-by: Gauthier Dandele <92022724+GogoVega@users.noreply.github.com> --- .../node_modules/@node-red/editor-client/src/js/ui/subflow.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js index 603dc8d30..3e1b9a410 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js @@ -512,7 +512,6 @@ RED.subflow = (function() { removedNodes.push(n); } if (n.z == id) { - RED.nodes.updateConfigNodeUsers(n, { action: "remove" }); removedNodes.push(n); } }); From 39a85c721d76d39ccdd3d804100a84261ec2f5ea Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 4 Dec 2024 13:10:48 +0000 Subject: [PATCH 077/121] Update packages/node_modules/@node-red/editor-client/src/js/history.js --- .../@node-red/editor-client/src/js/history.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/history.js b/packages/node_modules/@node-red/editor-client/src/js/history.js index 2b5b259e6..73193e5df 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/history.js +++ b/packages/node_modules/@node-red/editor-client/src/js/history.js @@ -491,7 +491,13 @@ RED.history = (function() { } }); } - ev.node[i] = ev.changes[i]; + if (i === "credentials" && ev.changes[i]) { + for (const [key, value] of Object.entries(ev.changes[i])) { + ev.node.credentials[key] = value; + } + } else { + ev.node[i] = ev.changes[i]; + } } } From 4e61c54be55643d9068703d95febeda53c60ff88 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 4 Dec 2024 15:49:39 +0000 Subject: [PATCH 078/121] Update packages/node_modules/@node-red/editor-client/src/js/ui/editor.js Co-authored-by: Gauthier Dandele <92022724+GogoVega@users.noreply.github.com> --- .../node_modules/@node-red/editor-client/src/js/ui/editor.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index 635bc8ca2..1ee73c6ea 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -855,9 +855,6 @@ RED.editor = (function() { for (const prop in editing_node._def.credentials) { if (Object.prototype.hasOwnProperty.call(editing_node._def.credentials, prop)) { if (oldCreds[prop] !== editing_node.credentials[prop]) { - if (editing_node.credentials[prop] === '__PWRD__') { - continue; - } editState.changes.credentials = editState.changes.credentials || {}; editState.changes.credentials[prop] = oldCreds[prop]; editState.changed = true; From bfd98aaf22e138f65a911bac1b0998af147fc72a Mon Sep 17 00:00:00 2001 From: Franck Date: Thu, 5 Dec 2024 12:24:02 +0100 Subject: [PATCH 079/121] PERF : make single buffer / string file reading faster --- .../node_modules/@node-red/nodes/core/storage/10-file.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/storage/10-file.js b/packages/node_modules/@node-red/nodes/core/storage/10-file.js index fea8c490e..5cff2b631 100644 --- a/packages/node_modules/@node-red/nodes/core/storage/10-file.js +++ b/packages/node_modules/@node-red/nodes/core/storage/10-file.js @@ -339,7 +339,7 @@ module.exports = function(RED) { } else { msg.filename = filename; - var lines = Buffer.from([]); + const bufferArray = []; var spare = ""; var count = 0; var type = "buffer"; @@ -397,7 +397,7 @@ module.exports = function(RED) { } } else { - lines = Buffer.concat([lines,chunk]); + bufferArray.push(chunk); } } }) @@ -413,10 +413,11 @@ module.exports = function(RED) { }) .on('end', function() { if (node.chunk === false) { + const buffer = Buffer.concat(bufferArray); if (node.format === "utf8") { - msg.payload = decode(lines, node.encoding); + msg.payload = decode(buffer, node.encoding); } - else { msg.payload = lines; } + else { msg.payload = buffer; } nodeSend(msg); } else if (node.format === "lines") { From 43a9a3c3b137e2344d1119c60f589ba263df94b3 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 5 Dec 2024 15:34:24 +0000 Subject: [PATCH 080/121] Apply zoom scale when calculating annotation positions Fixes #4978 --- .../editor-client/src/js/ui/view-annotations.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view-annotations.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view-annotations.js index b08df80ae..f4cda8d2c 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view-annotations.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view-annotations.js @@ -11,7 +11,7 @@ RED.view.annotations = (function() { } let badgeRDX = 0; let badgeLDX = 0; - + const scale = RED.view.scale() for (let i=0,l=evt.el.__annotations__.length;i Date: Thu, 5 Dec 2024 16:00:54 +0000 Subject: [PATCH 081/121] Ensure node.sep is honoured when generating CSV --- .../@node-red/nodes/core/parsers/70-CSV.js | 18 +++++++++--------- .../nodes/core/parsers/lib/csv/index.js | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js b/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js index c6a91d61a..92d12d0df 100644 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js +++ b/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js @@ -171,7 +171,7 @@ module.exports = function(RED) { } // join lines, don't forget to add the last new line msg.payload = ou.join(node.ret) + node.ret; - msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).join(','); + msg.columns = template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).join(node.sep); if (msg.payload !== '') { send(msg); } @@ -289,14 +289,14 @@ module.exports = function(RED) { } if (msg.parts.index + 1 === msg.parts.count) { msg.payload = node.store; - msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(','); + msg.columns = template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).filter(v => v).join(node.sep); delete msg.parts; send(msg); node.store = []; } } else { - msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(','); + msg.columns = template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).filter(v => v).join(node.sep); send(msg); // finally send the array } } @@ -304,7 +304,7 @@ module.exports = function(RED) { var len = a.length; for (var i = 0; i < len; i++) { var newMessage = RED.util.cloneMessage(msg); - newMessage.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(','); + newMessage.columns = template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).filter(v => v).join(node.sep); newMessage.payload = a[i]; if (!has_parts) { newMessage.parts = { @@ -367,7 +367,7 @@ module.exports = function(RED) { const sendHeadersAlways = node.hdrout === "all" const sendHeaders = !dontSendHeaders && (sendHeadersOnce || sendHeadersAlways) const quoteables = [node.sep, node.quo, "\n", "\r"] - const templateQuoteables = [',', '"', "\n", "\r"] + const templateQuoteables = [node.sep, '"', "\n", "\r"] let badTemplateWarnOnce = true const columnStringToTemplateArray = function (col, sep) { @@ -378,9 +378,9 @@ module.exports = function(RED) { } const templateArrayToColumnString = function (template, keepEmptyColumns) { // NOTE: enforce strict column template parsing in RFC4180 mode - const parsed = csv.parse('', {headers: template, headersOnly:true, separator: ',', quote: node.quo, outputStyle: 'array', strict: true }) + const parsed = csv.parse('', {headers: template, headersOnly:true, separator: node.sep, quote: node.quo, outputStyle: 'array', strict: true }) return keepEmptyColumns - ? parsed.headers.map(e => addQuotes(e || '', { separator: ',', quoteables: templateQuoteables})) + ? parsed.headers.map(e => addQuotes(e || '', { separator: node.sep, quoteables: templateQuoteables})).join(node.sep) : parsed.header // exclues empty columns // TODO: resolve inconsistency between CSV->JSON and JSON->CSV // CSV->JSON: empty columns are excluded @@ -441,7 +441,7 @@ module.exports = function(RED) { if (sendHeaders && node.hdrSent === false) { if (hasTemplate(template) === false) { if (msg.hasOwnProperty("columns")) { - template = columnStringToTemplateArray(msg.columns || "", ",") || [''] + template = columnStringToTemplateArray(msg.columns || "", node.sep) || [''] } else { template = Object.keys(inputData[0]) || [''] @@ -475,7 +475,7 @@ module.exports = function(RED) { } else { /*** row is an object ***/ if (hasTemplate(template) === false && (msg.hasOwnProperty("columns"))) { - template = columnStringToTemplateArray(msg.columns || "", ",") + template = columnStringToTemplateArray(msg.columns || "", node.sep) } if (hasTemplate(template) === false) { /*** row is an object but we still don't have a template ***/ diff --git a/packages/node_modules/@node-red/nodes/core/parsers/lib/csv/index.js b/packages/node_modules/@node-red/nodes/core/parsers/lib/csv/index.js index 73cf4b292..dd2fb2db9 100644 --- a/packages/node_modules/@node-red/nodes/core/parsers/lib/csv/index.js +++ b/packages/node_modules/@node-red/nodes/core/parsers/lib/csv/index.js @@ -200,9 +200,9 @@ function parse(csvIn, parseOptions) { if (!headers[i]) { continue } - quotedHeaders.push(quoteCell(headers[i], { quote, separator: ',' })) + quotedHeaders.push(quoteCell(headers[i], { quote, separator })) } - finalResult.header = quotedHeaders.join(',') // always quote headers and join with comma + finalResult.header = quotedHeaders.join(separator) // always quote headers and join with comma // output is an array of arrays [[],[],[]] if (ouputArrays || headersOnly) { From 66bd1feb47b7b4fc2982a6daef21ee39fce772f9 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 6 Dec 2024 13:45:48 +0000 Subject: [PATCH 082/121] Apply suggestions from code review Co-authored-by: Gauthier Dandele <92022724+GogoVega@users.noreply.github.com> --- .../node_modules/@node-red/editor-client/src/js/history.js | 6 ++++++ .../@node-red/editor-client/src/js/ui/editor.js | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/packages/node_modules/@node-red/editor-client/src/js/history.js b/packages/node_modules/@node-red/editor-client/src/js/history.js index 73193e5df..d4fbc5102 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/history.js +++ b/packages/node_modules/@node-red/editor-client/src/js/history.js @@ -492,8 +492,14 @@ RED.history = (function() { }); } if (i === "credentials" && ev.changes[i]) { + // Reset - Only want to keep the changes + inverseEv.changes[i] = {}; for (const [key, value] of Object.entries(ev.changes[i])) { + inverseEv.changes[i][key] = ev.node.credentials[key]; ev.node.credentials[key] = value; + if (ev.node._def.credentials[key]?.type === 'password') { + ev.node.credentials['has_' + key] = !!value; + } } } else { ev.node[i] = ev.changes[i]; diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index 1ee73c6ea..bb4457f8c 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -855,6 +855,12 @@ RED.editor = (function() { for (const prop in editing_node._def.credentials) { if (Object.prototype.hasOwnProperty.call(editing_node._def.credentials, prop)) { if (oldCreds[prop] !== editing_node.credentials[prop]) { + if (editing_node.credentials[prop] === '__PWRD__') { + // The password may not exist in oldCreds + // The value '__PWRD__' means the password exists, + // so ignore this change + continue; + } editState.changes.credentials = editState.changes.credentials || {}; editState.changes.credentials[prop] = oldCreds[prop]; editState.changed = true; From 89e40a0b8fe49929cebc578e6f5e45f3bf427057 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 6 Dec 2024 16:15:37 +0000 Subject: [PATCH 083/121] Rework saving of credentials to undo history --- .../@node-red/editor-client/src/js/history.js | 9 +++--- .../editor-client/src/js/ui/editor.js | 29 ------------------- .../src/js/ui/editors/panes/properties.js | 1 + 3 files changed, 6 insertions(+), 33 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/history.js b/packages/node_modules/@node-red/editor-client/src/js/history.js index d4fbc5102..2b95b35d3 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/history.js +++ b/packages/node_modules/@node-red/editor-client/src/js/history.js @@ -495,11 +495,12 @@ RED.history = (function() { // Reset - Only want to keep the changes inverseEv.changes[i] = {}; for (const [key, value] of Object.entries(ev.changes[i])) { - inverseEv.changes[i][key] = ev.node.credentials[key]; - ev.node.credentials[key] = value; - if (ev.node._def.credentials[key]?.type === 'password') { - ev.node.credentials['has_' + key] = !!value; + // Edge case: node.credentials is cleared after a deploy, so we can't + // capture values for the inverse event when undoing past a deploy + if (ev.node.credentials) { + inverseEv.changes[i][key] = ev.node.credentials[key]; } + ev.node.credentials[key] = value; } } else { ev.node[i] = ev.changes[i]; diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index bb4457f8c..fbb822418 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -808,17 +808,6 @@ RED.editor = (function() { } } - const oldCreds = {}; - if (editing_node._def.credentials) { - for (const prop in editing_node._def.credentials) { - if (Object.prototype.hasOwnProperty.call(editing_node._def.credentials, prop)) { - if (prop in editing_node.credentials) { - oldCreds[prop] = editing_node.credentials[prop]; - } - } - } - } - try { const rc = editing_node._def.oneditsave.call(editing_node); if (rc === true) { @@ -850,24 +839,6 @@ RED.editor = (function() { } } } - - if (editing_node._def.credentials) { - for (const prop in editing_node._def.credentials) { - if (Object.prototype.hasOwnProperty.call(editing_node._def.credentials, prop)) { - if (oldCreds[prop] !== editing_node.credentials[prop]) { - if (editing_node.credentials[prop] === '__PWRD__') { - // The password may not exist in oldCreds - // The value '__PWRD__' means the password exists, - // so ignore this change - continue; - } - editState.changes.credentials = editState.changes.credentials || {}; - editState.changes.credentials[prop] = oldCreds[prop]; - editState.changed = true; - } - } - } - } } } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js index 396317052..88472f97e 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/properties.js @@ -195,6 +195,7 @@ // Like the user sets a value, saves the config, // reopens the config and save the config again } else { + changes['has_' + cred] = node.credentials['has_' + cred]; changes[cred] = node.credentials[cred]; node.credentials[cred] = value; } From 56a4530ec65f7004e2ae05ff4ef06fd4017035d0 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Fri, 6 Dec 2024 22:22:37 +0000 Subject: [PATCH 084/121] Fix delay node not dropping when nodeMessageBufferMaxLength is set (#4973) * Fix delay node not dropping when nodeMessageBufferMaxLength is set to close #4966 * Rmove redundant codes * Tidy up code removal --------- Co-authored-by: Nick O'Leary --- .../@node-red/nodes/core/function/89-delay.js | 52 ++++++------------- 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/function/89-delay.js b/packages/node_modules/@node-red/nodes/core/function/89-delay.js index 8f9964115..17cbd2f4f 100644 --- a/packages/node_modules/@node-red/nodes/core/function/89-delay.js +++ b/packages/node_modules/@node-red/nodes/core/function/89-delay.js @@ -291,43 +291,23 @@ module.exports = function(RED) { } } else if (!msg.hasOwnProperty("reset")) { - if (maxKeptMsgsCount(node) > 0) { - if (node.intervalID === -1) { - node.send(msg); - node.intervalID = setInterval(sendMsgFromBuffer, node.rate); - } else { - if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate)) && node.rate !== msg.rate) { - node.rate = msg.rate; - clearInterval(node.intervalID); - node.intervalID = setInterval(sendMsgFromBuffer, node.rate); - } - if (node.buffer.length < _maxKeptMsgsCount) { - var m = RED.util.cloneMessage(msg); - node.buffer.push({msg: m, send: send, done: done}); - } else { - node.trace("dropped due to buffer overflow. msg._msgid = " + msg._msgid); - node.droppedMsgs++; - } - } + if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate))) { + node.rate = msg.rate; } - else { - if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate))) { - node.rate = msg.rate; - } - var timeSinceLast; - if (node.lastSent) { - timeSinceLast = process.hrtime(node.lastSent); - } - if (!node.lastSent) { // ensuring that we always send the first message - node.lastSent = process.hrtime(); - send(msg); - } - else if ( ( (timeSinceLast[0] * SECONDS_TO_NANOS) + timeSinceLast[1] ) > (node.rate * MILLIS_TO_NANOS) ) { - node.lastSent = process.hrtime(); - send(msg); - } else if (node.outputs === 2) { - send([null,msg]) - } + var timeSinceLast; + if (node.lastSent) { + timeSinceLast = process.hrtime(node.lastSent); + } + if (!node.lastSent) { // ensuring that we always send the first message + node.lastSent = process.hrtime(); + send(msg); + } + else if ( ( (timeSinceLast[0] * SECONDS_TO_NANOS) + timeSinceLast[1] ) > (node.rate * MILLIS_TO_NANOS) ) { + node.lastSent = process.hrtime(); + send(msg); + } + else if (node.outputs === 2) { + send([null,msg]) } done(); } From 00a30109338ed57356dcf095b88d30ce719842b6 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 10 Dec 2024 15:25:23 +0000 Subject: [PATCH 085/121] Update dependencies --- package.json | 2 +- packages/node_modules/@node-red/editor-api/package.json | 2 +- packages/node_modules/@node-red/runtime/package.json | 2 +- packages/node_modules/node-red/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index c7fe644f7..e0904530d 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "cors": "2.8.5", "cronosjs": "1.7.1", "denque": "2.1.0", - "express": "4.21.1", + "express": "4.21.2", "express-session": "1.18.1", "form-data": "4.0.0", "fs-extra": "11.2.0", diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json index e58f2e9fb..7f71a5fb7 100644 --- a/packages/node_modules/@node-red/editor-api/package.json +++ b/packages/node_modules/@node-red/editor-api/package.json @@ -23,7 +23,7 @@ "clone": "2.1.2", "cors": "2.8.5", "express-session": "1.18.1", - "express": "4.21.1", + "express": "4.21.2", "memorystore": "1.6.7", "mime": "3.0.0", "multer": "1.4.5-lts.1", diff --git a/packages/node_modules/@node-red/runtime/package.json b/packages/node_modules/@node-red/runtime/package.json index a160c402c..73e13f13f 100644 --- a/packages/node_modules/@node-red/runtime/package.json +++ b/packages/node_modules/@node-red/runtime/package.json @@ -20,7 +20,7 @@ "@node-red/util": "4.0.5", "async-mutex": "0.5.0", "clone": "2.1.2", - "express": "4.21.1", + "express": "4.21.2", "fs-extra": "11.2.0", "json-stringify-safe": "5.0.1", "rfdc": "^1.3.1" diff --git a/packages/node_modules/node-red/package.json b/packages/node_modules/node-red/package.json index 0969024ff..56711d6ff 100644 --- a/packages/node_modules/node-red/package.json +++ b/packages/node_modules/node-red/package.json @@ -38,7 +38,7 @@ "basic-auth": "2.0.1", "bcryptjs": "2.4.3", "cors": "2.8.5", - "express": "4.21.1", + "express": "4.21.2", "fs-extra": "11.2.0", "node-red-admin": "^4.0.1", "nopt": "5.0.0", From 7d284ce1575190f937b50b6cb4da12cf0a5749a5 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 10 Dec 2024 15:42:42 +0000 Subject: [PATCH 086/121] Update packages/node_modules/@node-red/editor-client/src/js/ui/editor.js Co-authored-by: Gauthier Dandele <92022724+GogoVega@users.noreply.github.com> --- .../@node-red/editor-client/src/js/ui/editor.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index fbb822418..37dabe663 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -808,6 +808,20 @@ RED.editor = (function() { } } + const oldCreds = {}; + if (editing_node._def.credentials) { + for (const prop in editing_node._def.credentials) { + if (Object.prototype.hasOwnProperty.call(editing_node._def.credentials, prop)) { + if (editing_node._def.credentials[prop].type === 'password') { + oldCreds['has_' + prop] = editing_node.credentials['has_' + prop]; + } + if (prop in editing_node.credentials) { + oldCreds[prop] = editing_node.credentials[prop]; + } + } + } + } + try { const rc = editing_node._def.oneditsave.call(editing_node); if (rc === true) { From 11c4277466238e5e053cb9087fc975bdd69284dd Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 10 Dec 2024 15:42:59 +0000 Subject: [PATCH 087/121] Update packages/node_modules/@node-red/editor-client/src/js/ui/editor.js Co-authored-by: Gauthier Dandele <92022724+GogoVega@users.noreply.github.com> --- .../editor-client/src/js/ui/editor.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index 37dabe663..ca74b3eff 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -853,6 +853,25 @@ RED.editor = (function() { } } } + + if (editing_node._def.credentials) { + for (const prop in editing_node._def.credentials) { + if (Object.prototype.hasOwnProperty.call(editing_node._def.credentials, prop)) { + if (oldCreds[prop] !== editing_node.credentials[prop]) { + if (editing_node.credentials[prop] === '__PWRD__') { + // The password may not exist in oldCreds + // The value '__PWRD__' means the password exists, + // so ignore this change + continue; + } + editState.changes.credentials = editState.changes.credentials || {}; + editState.changes.credentials['has_' + prop] = oldCreds['has_' + prop]; + editState.changes.credentials[prop] = oldCreds[prop]; + editState.changed = true; + } + } + } + } } } From 2c3fbb14679534cc0a399316619cd32a30a1b976 Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Thu, 12 Dec 2024 16:40:43 +0000 Subject: [PATCH 088/121] revert changes to legacy mode --- .../node_modules/@node-red/nodes/core/parsers/70-CSV.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js b/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js index 92d12d0df..38cda9a6f 100644 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js +++ b/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js @@ -171,7 +171,7 @@ module.exports = function(RED) { } // join lines, don't forget to add the last new line msg.payload = ou.join(node.ret) + node.ret; - msg.columns = template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).join(node.sep); + msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).join(','); if (msg.payload !== '') { send(msg); } @@ -289,14 +289,14 @@ module.exports = function(RED) { } if (msg.parts.index + 1 === msg.parts.count) { msg.payload = node.store; - msg.columns = template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).filter(v => v).join(node.sep); + msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(','); delete msg.parts; send(msg); node.store = []; } } else { - msg.columns = template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).filter(v => v).join(node.sep); + msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(','); send(msg); // finally send the array } } @@ -304,7 +304,7 @@ module.exports = function(RED) { var len = a.length; for (var i = 0; i < len; i++) { var newMessage = RED.util.cloneMessage(msg); - newMessage.columns = template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).filter(v => v).join(node.sep); + newMessage.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(','); newMessage.payload = a[i]; if (!has_parts) { newMessage.parts = { From 6af3c8c2a9422e22129b62ee5f211b9ca5964a83 Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Thu, 12 Dec 2024 16:41:26 +0000 Subject: [PATCH 089/121] revert changes to csv parser --- .../@node-red/nodes/core/parsers/lib/csv/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/parsers/lib/csv/index.js b/packages/node_modules/@node-red/nodes/core/parsers/lib/csv/index.js index dd2fb2db9..73cf4b292 100644 --- a/packages/node_modules/@node-red/nodes/core/parsers/lib/csv/index.js +++ b/packages/node_modules/@node-red/nodes/core/parsers/lib/csv/index.js @@ -200,9 +200,9 @@ function parse(csvIn, parseOptions) { if (!headers[i]) { continue } - quotedHeaders.push(quoteCell(headers[i], { quote, separator })) + quotedHeaders.push(quoteCell(headers[i], { quote, separator: ',' })) } - finalResult.header = quotedHeaders.join(separator) // always quote headers and join with comma + finalResult.header = quotedHeaders.join(',') // always quote headers and join with comma // output is an array of arrays [[],[],[]] if (ouputArrays || headersOnly) { From b139eb4a18fbfc36793f8bd0b4f088f6642fe7fa Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Thu, 12 Dec 2024 16:42:11 +0000 Subject: [PATCH 090/121] update CSV to adhere to strict rfc compliance on msg.columns --- .../@node-red/nodes/core/parsers/70-CSV.js | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js b/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js index 38cda9a6f..26fdb636f 100644 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js +++ b/packages/node_modules/@node-red/nodes/core/parsers/70-CSV.js @@ -367,20 +367,21 @@ module.exports = function(RED) { const sendHeadersAlways = node.hdrout === "all" const sendHeaders = !dontSendHeaders && (sendHeadersOnce || sendHeadersAlways) const quoteables = [node.sep, node.quo, "\n", "\r"] - const templateQuoteables = [node.sep, '"', "\n", "\r"] + const templateQuoteables = [node.sep, node.quo, "\n", "\r"] + const templateQuoteablesStrict = [',', '"', "\n", "\r"] let badTemplateWarnOnce = true const columnStringToTemplateArray = function (col, sep) { // NOTE: enforce strict column template parsing in RFC4180 mode const parsed = csv.parse(col, { separator: sep, quote: node.quo, outputStyle: 'array', strict: true }) - if (parsed.headers.length > 0) { node.goodtmpl = true } else { node.goodtmpl = false } - return parsed.headers.length ? parsed.headers : null + if (parsed.data?.length === 1) { node.goodtmpl = true } else { node.goodtmpl = false } + return node.goodtmpl ? parsed.data[0] : null } - const templateArrayToColumnString = function (template, keepEmptyColumns) { - // NOTE: enforce strict column template parsing in RFC4180 mode - const parsed = csv.parse('', {headers: template, headersOnly:true, separator: node.sep, quote: node.quo, outputStyle: 'array', strict: true }) + const templateArrayToColumnString = function (template, keepEmptyColumns, separator = ',', quotables = templateQuoteablesStrict) { + // NOTE: defaults to strict column template parsing (commas and double quotes) + const parsed = csv.parse('', {headers: template, headersOnly:true, separator, quote: node.quo, outputStyle: 'array', strict: true }) return keepEmptyColumns - ? parsed.headers.map(e => addQuotes(e || '', { separator: node.sep, quoteables: templateQuoteables})).join(node.sep) + ? parsed.headers.map(e => addQuotes(e || '', { separator, quoteables: quotables })).join(separator) : parsed.header // exclues empty columns // TODO: resolve inconsistency between CSV->JSON and JSON->CSV // CSV->JSON: empty columns are excluded @@ -441,13 +442,13 @@ module.exports = function(RED) { if (sendHeaders && node.hdrSent === false) { if (hasTemplate(template) === false) { if (msg.hasOwnProperty("columns")) { - template = columnStringToTemplateArray(msg.columns || "", node.sep) || [''] + template = columnStringToTemplateArray(msg.columns || "", ",") || [''] } else { template = Object.keys(inputData[0]) || [''] } } - stringBuilder.push(templateArrayToColumnString(template, true)) + stringBuilder.push(templateArrayToColumnString(template, true, node.sep, templateQuoteables)) // use user set separator for output data. if (sendHeadersOnce) { node.hdrSent = true } } @@ -475,7 +476,7 @@ module.exports = function(RED) { } else { /*** row is an object ***/ if (hasTemplate(template) === false && (msg.hasOwnProperty("columns"))) { - template = columnStringToTemplateArray(msg.columns || "", node.sep) + template = columnStringToTemplateArray(msg.columns || "", ",") } if (hasTemplate(template) === false) { /*** row is an object but we still don't have a template ***/ @@ -483,6 +484,7 @@ module.exports = function(RED) { node.warn(RED._("csv.errors.obj_csv")) badTemplateWarnOnce = false } + template = Object.keys(row) || [''] const rowData = [] for (let header in inputData[0]) { if (row.hasOwnProperty(header)) { @@ -518,7 +520,7 @@ module.exports = function(RED) { // join lines, don't forget to add the last new line msg.payload = stringBuilder.join(node.ret) + node.ret - msg.columns = templateArrayToColumnString(template) + msg.columns = templateArrayToColumnString(template) // always strict commas + double quotes for if (msg.payload !== '') { send(msg) } done() } @@ -615,16 +617,15 @@ module.exports = function(RED) { } if (msg.parts.index + 1 === msg.parts.count) { msg.payload = node.store - msg.columns = csvParseResult.header - // msg._mode = 'RFC4180 mode' + // msg.columns = csvParseResult.header + msg.columns = templateArrayToColumnString(csvParseResult.headers) // always strict commas + double quotes for msg.columns delete msg.parts send(msg) node.store = [] } } else { - msg.columns = csvParseResult.header - // msg._mode = 'RFC4180 mode' + msg.columns = templateArrayToColumnString(csvParseResult.headers) // always strict commas + double quotes for msg.columns msg.payload = data send(msg); // finally send the array } @@ -633,7 +634,8 @@ module.exports = function(RED) { const len = data.length for (let row = 0; row < len; row++) { const newMessage = RED.util.cloneMessage(msg) - newMessage.columns = csvParseResult.header + // newMessage.columns = csvParseResult.header + newMessage.columns = templateArrayToColumnString(csvParseResult.headers) // always strict commas + double quotes for msg.columns newMessage.payload = data[row] if (!has_parts) { newMessage.parts = { From 82c756b091cd2683e9f4b95f76292051937439e4 Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Thu, 12 Dec 2024 16:43:38 +0000 Subject: [PATCH 091/121] add test for correct CSV output when separator in non-comma --- test/nodes/core/parsers/70-CSV_spec.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/nodes/core/parsers/70-CSV_spec.js b/test/nodes/core/parsers/70-CSV_spec.js index f362ecdbf..b7ba60f58 100644 --- a/test/nodes/core/parsers/70-CSV_spec.js +++ b/test/nodes/core/parsers/70-CSV_spec.js @@ -2077,6 +2077,26 @@ describe('CSV node (RFC Mode)', function () { }); }); + it('should convert a simple object back to a tsv with headers using a tab as a separator', function (done) { + const flow = [{ id: "n1", type: "csv", spec: "rfc", temp: "", sep: "\t", ret: '\n', hdrout: "all", wires: [["n2"]] }, // RFC-vs-Legacy difference - use line separator \n to satisfy original test + { id: "n2", type: "helper" }]; + helper.load(csvNode, flow, function () { + const n1 = helper.getNode("n1"); + const n2 = helper.getNode("n2"); + n2.on("input", function (msg) { + try { + msg.should.have.property('payload', 'd\tb\tc\ta\n1\tfoo\t"ba""r"\tdi,ng\n'); + msg.should.have.property('columns', 'd,b,c,a'); // Strict RFC columns + done(); + } catch (e) { + done(e); + } + }); + const testJson = { d: 1, b: "foo", c: "ba\"r", a: "di,ng" }; + n1.emit("input", { payload: testJson }); + }); + }); + it('should handle a template with spaces in the property names', function (done) { const flow = [{ id: "n1", type: "csv", spec: "rfc", temp: "a,b o,c p,,e", ret: '\n', wires: [["n2"]] }, // RFC-vs-Legacy difference - use line separator \n to satisfy original test { id: "n2", type: "helper" }]; From 16005a462d4bd32aeba770d663f7be5ecf0bba2d Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Thu, 12 Dec 2024 16:44:16 +0000 Subject: [PATCH 092/121] update tests to check msg.columns is strictly RFC compliant --- test/nodes/core/parsers/70-CSV_spec.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/nodes/core/parsers/70-CSV_spec.js b/test/nodes/core/parsers/70-CSV_spec.js index b7ba60f58..9f6749b9f 100644 --- a/test/nodes/core/parsers/70-CSV_spec.js +++ b/test/nodes/core/parsers/70-CSV_spec.js @@ -2067,6 +2067,7 @@ describe('CSV node (RFC Mode)', function () { n2.on("input", function (msg) { try { msg.should.have.property('payload', '1\tfoo\t"ba""r"\tdi,ng\n'); + msg.should.have.property('columns', 'd,b,c,a'); // Strict RFC columns done(); } catch (e) { done(e); @@ -2106,6 +2107,7 @@ describe('CSV node (RFC Mode)', function () { n2.on("input", function (msg) { try { msg.should.have.property('payload', '4,foo,true,,0\n'); + msg.should.have.property('columns', 'a,b o,c p,e'); // Strict RFC columns done(); } catch (e) { done(e); @@ -2126,6 +2128,7 @@ describe('CSV node (RFC Mode)', function () { try { // 'payload', 'a"a,b\'b\nA1,B1\nA2,B2\n'); // Legacy msg.should.have.property('payload', '"a""a",b\'b\nA1,B1\nA2,B2\n'); // RFC-vs-Legacy difference - RFC4180 Section 2.6, 2.7 quote handling + msg.should.have.property('columns', '"a""a",b\'b'); // RCF compliant column names done(); } catch (e) { done(e); @@ -2191,6 +2194,7 @@ describe('CSV node (RFC Mode)', function () { n2.on("input", function (msg) { try { msg.should.have.property('payload', '1,3,2,4\n4,2,3,1\n'); + msg.should.have.property('columns', 'd,b,c,a'); // Strict RFC columns done(); } catch (e) { done(e); } @@ -2209,6 +2213,7 @@ describe('CSV node (RFC Mode)', function () { n2.on("input", function (msg) { try { msg.should.have.property('payload', 'd,b,c,a\n1,3,2,4\n4,"f\ng",3,1\n'); + msg.should.have.property('columns', 'd,b,c,a'); // Strict RFC columns done(); } catch (e) { done(e); } @@ -2228,6 +2233,7 @@ describe('CSV node (RFC Mode)', function () { try { // 'payload', ',0,1,foo,"ba""r","di,ng","fa\nba"\n'); msg.should.have.property('payload', ',0,1,foo\n'); // RFC-vs-Legacy difference - respect that user has specified a template with 4 columns + msg.should.have.property('columns', 'a,b,c,d'); done(); } catch (e) { done(e); } @@ -2347,6 +2353,7 @@ describe('CSV node (RFC Mode)', function () { n2.on("input", function (msg) { try { msg.should.have.property('payload', '{},"text,with,commas","This ""is"" a banana","{""sub"":""object""}"\n'); + msg.should.have.property('columns', 'a,b,c,d'); done(); } catch (e) { done(e); } From e9d5d20e2da0f475f2e8da00d5b0e6fb19df8610 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 16 Dec 2024 11:35:14 +0000 Subject: [PATCH 093/121] Update for 4.0.6 --- CHANGELOG.md | 33 +++++++++++++++++++ package.json | 2 +- .../@node-red/editor-api/package.json | 6 ++-- .../@node-red/editor-client/package.json | 2 +- .../node_modules/@node-red/nodes/package.json | 2 +- .../@node-red/registry/package.json | 4 +-- .../@node-red/runtime/package.json | 6 ++-- .../node_modules/@node-red/util/package.json | 2 +- packages/node_modules/node-red/package.json | 10 +++--- 9 files changed, 50 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b55ac4c6..db473c61b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,36 @@ +#### 4.0.6: Maintenance Release + +Editor + + - Roll up various fixes on config node change history (#4975) @knolleary + - Add quotes when installing local tgz to fix spacing in the file path (#4949) @AGhorab-upland + - Validate json dropped into editor to avoid unhelpful error messages (#4964) @knolleary + - Fix junction insert position via context menu (#4974) @knolleary + - Apply zoom scale when calculating annotation positions (#4981) @knolleary + - Handle the import of an incomplete Subflow (#4811) @GogoVega + - Fix updating the Subflow name during a copy (#4809) @GogoVega + - Rename variable to avoid confusion in view.js (#4963) @knolleary + - Change groups.length to groups.size (#4959) @hungtcs + - Remove disabled node types from QuickAddDialog list (#4946) @GogoVega + - Fix `setModulePendingUpdated` with plugins (#4939) @GogoVega + - Missing getSubscriptions in the docs while its implemented (#4934) @ersinpw + - Apply `envVarExcludes` setting to `util.getSetting` into the function node (#4925) @GogoVega + - Fix `envVar` editable list should be sortable (#4932) @GogoVega + - Improve the node name auto-generated with the first available number (#4912) @GogoVega + +Runtime + + - Get the env config node from the parent subflow (#4960) @GogoVega + - Update dependencies (#4987) @knolleary + +Nodes + + - Performance : make reading single buffer / string file faster by not re-allocating and handling huge buffers (#4980) @Fadoli + - Make delay node rate limit reset consistent - not send on reset. (#4940) @dceejay + - Fix trigger node date handling for latest time type input (#4915) @dceejay + - Fix delay node not dropping when nodeMessageBufferMaxLength is set (#4973) + - Ensure node.sep is honoured when generating CSV (#4982) @knolleary + #### 4.0.5: Maintenance Release Editor diff --git a/package.json b/package.json index e0904530d..679474fc8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "4.0.5", + "version": "4.0.6", "description": "Low-code programming for event-driven applications", "homepage": "https://nodered.org", "license": "Apache-2.0", diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json index 7f71a5fb7..14fdfb77a 100644 --- a/packages/node_modules/@node-red/editor-api/package.json +++ b/packages/node_modules/@node-red/editor-api/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-api", - "version": "4.0.5", + "version": "4.0.6", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/util": "4.0.5", - "@node-red/editor-client": "4.0.5", + "@node-red/util": "4.0.6", + "@node-red/editor-client": "4.0.6", "bcryptjs": "2.4.3", "body-parser": "1.20.3", "clone": "2.1.2", diff --git a/packages/node_modules/@node-red/editor-client/package.json b/packages/node_modules/@node-red/editor-client/package.json index b6b7a2c73..3d13bdc85 100644 --- a/packages/node_modules/@node-red/editor-client/package.json +++ b/packages/node_modules/@node-red/editor-client/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-client", - "version": "4.0.5", + "version": "4.0.6", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json index 84db88466..9659717fb 100644 --- a/packages/node_modules/@node-red/nodes/package.json +++ b/packages/node_modules/@node-red/nodes/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/nodes", - "version": "4.0.5", + "version": "4.0.6", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/registry/package.json b/packages/node_modules/@node-red/registry/package.json index 9437a5beb..85058d0a7 100644 --- a/packages/node_modules/@node-red/registry/package.json +++ b/packages/node_modules/@node-red/registry/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/registry", - "version": "4.0.5", + "version": "4.0.6", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,7 +16,7 @@ } ], "dependencies": { - "@node-red/util": "4.0.5", + "@node-red/util": "4.0.6", "clone": "2.1.2", "fs-extra": "11.2.0", "semver": "7.6.3", diff --git a/packages/node_modules/@node-red/runtime/package.json b/packages/node_modules/@node-red/runtime/package.json index 73e13f13f..65ffc47a6 100644 --- a/packages/node_modules/@node-red/runtime/package.json +++ b/packages/node_modules/@node-red/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/runtime", - "version": "4.0.5", + "version": "4.0.6", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/registry": "4.0.5", - "@node-red/util": "4.0.5", + "@node-red/registry": "4.0.6", + "@node-red/util": "4.0.6", "async-mutex": "0.5.0", "clone": "2.1.2", "express": "4.21.2", diff --git a/packages/node_modules/@node-red/util/package.json b/packages/node_modules/@node-red/util/package.json index c02ab3805..3338acd5f 100644 --- a/packages/node_modules/@node-red/util/package.json +++ b/packages/node_modules/@node-red/util/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/util", - "version": "4.0.5", + "version": "4.0.6", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/node-red/package.json b/packages/node_modules/node-red/package.json index 56711d6ff..35b93bc93 100644 --- a/packages/node_modules/node-red/package.json +++ b/packages/node_modules/node-red/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "4.0.5", + "version": "4.0.6", "description": "Low-code programming for event-driven applications", "homepage": "https://nodered.org", "license": "Apache-2.0", @@ -31,10 +31,10 @@ "flow" ], "dependencies": { - "@node-red/editor-api": "4.0.5", - "@node-red/runtime": "4.0.5", - "@node-red/util": "4.0.5", - "@node-red/nodes": "4.0.5", + "@node-red/editor-api": "4.0.6", + "@node-red/runtime": "4.0.6", + "@node-red/util": "4.0.6", + "@node-red/nodes": "4.0.6", "basic-auth": "2.0.1", "bcryptjs": "2.4.3", "cors": "2.8.5", From b7e96ce6bc1009af2b30b805167b091528be2987 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 16 Dec 2024 16:58:13 +0000 Subject: [PATCH 094/121] Support custom login message and button --- .../@node-red/editor-api/lib/auth/index.js | 8 +++++ .../@node-red/editor-api/lib/editor/theme.js | 16 +++++++++- .../@node-red/editor-client/src/js/user.js | 31 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-api/lib/auth/index.js b/packages/node_modules/@node-red/editor-api/lib/auth/index.js index 30ff06756..9581983cb 100644 --- a/packages/node_modules/@node-red/editor-api/lib/auth/index.js +++ b/packages/node_modules/@node-red/editor-api/lib/auth/index.js @@ -126,6 +126,14 @@ async function login(req,res) { if (themeContext.login && themeContext.login.image) { response.image = themeContext.login.image; } + if (themeContext.login?.message) { + response.loginMessage = themeContext.login?.message + } + if (themeContext.login?.button) { + response.prompts = [ + { type: "button", ...themeContext.login.button } + ] + } } res.json(response); } diff --git a/packages/node_modules/@node-red/editor-api/lib/editor/theme.js b/packages/node_modules/@node-red/editor-api/lib/editor/theme.js index 2bbcbcd97..782a9d173 100644 --- a/packages/node_modules/@node-red/editor-api/lib/editor/theme.js +++ b/packages/node_modules/@node-red/editor-api/lib/editor/theme.js @@ -206,14 +206,28 @@ module.exports = { } if (theme.login) { + let themeContextLogin = {} + let hasLoginTheme = false if (theme.login.image) { url = serveFile(themeApp,"/login/",theme.login.image); if (url) { - themeContext.login = { + themeContextLogin.login = { image: url } + hasLoginTheme = true } } + if (theme.login.message) { + themeContextLogin.message = theme.login.message + hasLoginTheme = true + } + if (theme.login.button) { + themeContextLogin.button = theme.login.button + hasLoginTheme = true + } + if (hasLoginTheme) { + themeContext.login = themeContextLogin + } } themeApp.get("/", async function(req,res) { const themePluginList = await runtimeAPI.plugins.getPluginsByType({type:"node-red-theme"}); diff --git a/packages/node_modules/@node-red/editor-client/src/js/user.js b/packages/node_modules/@node-red/editor-client/src/js/user.js index 2deda8970..e2c3cb577 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/user.js +++ b/packages/node_modules/@node-red/editor-client/src/js/user.js @@ -168,6 +168,37 @@ RED.user = (function() { } + } else { + if (data.prompts) { + if (data.loginMessage) { + const sessionMessages = $("
    ",{class:"form-row",style:"text-align: center"}).appendTo("#node-dialog-login-fields"); + $('
    ').text(data.loginMessage).appendTo(sessionMessages); + } + + i = 0; + for (;i",{class:"form-row",style:"text-align: center"}).appendTo("#node-dialog-login-fields"); + var loginButton = $('',{style: "padding: 10px"}).appendTo(row).on("click", function() { + document.location = field.url; + }); + if (field.image) { + $("",{src:field.image}).appendTo(loginButton); + } else if (field.label) { + var label = $('').text(field.label); + if (field.icon) { + $('',{class: "fa fa-2x "+field.icon, style:"vertical-align: middle"}).appendTo(loginButton); + label.css({ + "verticalAlign":"middle", + "marginLeft":"8px" + }); + + } + label.appendTo(loginButton); + } + loginButton.button(); + } + } } if (opts.cancelable) { $("#node-dialog-login-cancel").button().on("click", function( event ) { From 1a47e2fc76890c0be3a20a766100848346b3e237 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 16 Dec 2024 17:09:31 +0000 Subject: [PATCH 095/121] Fix login image auth setting --- .../node_modules/@node-red/editor-api/lib/editor/theme.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/node_modules/@node-red/editor-api/lib/editor/theme.js b/packages/node_modules/@node-red/editor-api/lib/editor/theme.js index 782a9d173..cd1d569ac 100644 --- a/packages/node_modules/@node-red/editor-api/lib/editor/theme.js +++ b/packages/node_modules/@node-red/editor-api/lib/editor/theme.js @@ -211,9 +211,7 @@ module.exports = { if (theme.login.image) { url = serveFile(themeApp,"/login/",theme.login.image); if (url) { - themeContextLogin.login = { - image: url - } + themeContextLogin.image = url hasLoginTheme = true } } From 7785ce0dc0ca6b6973f7390bfcf61ac024817420 Mon Sep 17 00:00:00 2001 From: GogoVega <92022724+GogoVega@users.noreply.github.com> Date: Tue, 17 Dec 2024 15:36:51 +0100 Subject: [PATCH 096/121] Fix the user list of nested config node --- .../@node-red/editor-client/src/js/nodes.js | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 366120796..4042f3e12 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -707,12 +707,15 @@ RED.nodes = (function() { } n["_"] = RED._; } + + // Both node and config node can use a config node + updateConfigNodeUsers(newNode, { action: "add" }); + if (n._def.category == "config") { configNodes[n.id] = newNode; } else { if (n.wires && (n.wires.length > n.outputs)) { n.outputs = n.wires.length; } n.dirty = true; - updateConfigNodeUsers(newNode, { action: "add" }); if (n._def.category == "subflows" && typeof n.i === "undefined") { var nextId = 0; RED.nodes.eachNode(function(node) { @@ -774,9 +777,11 @@ RED.nodes = (function() { var removedLinks = []; var removedNodes = []; var node; + if (id in configNodes) { node = configNodes[id]; delete configNodes[id]; + updateConfigNodeUsers(node, { action: "remove" }); RED.events.emit('nodes:remove',node); RED.workspaces.refresh(); } else if (allNodes.hasNode(id)) { @@ -786,6 +791,8 @@ RED.nodes = (function() { removedLinks = links.filter(function(l) { return (l.source === node) || (l.target === node); }); removedLinks.forEach(removeLink); updateConfigNodeUsers(node, { action: "remove" }); + + // TODO: Legacy code for exclusive config node var updatedConfigNode = false; for (var d in node._def.defaults) { if (node._def.defaults.hasOwnProperty(d)) { @@ -2187,6 +2194,21 @@ RED.nodes = (function() { } } + // Config node can use another config node, must ensure that this other + // config node is added before to exists when updating the user list + const configNodeFilter = function (node) { + let count = 0; + if (node._def?.defaults) { + for (const def of Object.values(node._def.defaults)) { + if (def.type) { + count++; + } + } + } + return count; + }; + new_nodes.sort((a, b) => configNodeFilter(a) - configNodeFilter(b)); + // Find regular flow nodes and subflow instances for (i=0;i Date: Tue, 17 Dec 2024 17:57:01 +0100 Subject: [PATCH 097/121] Fix def can be undefined if the type is missing --- packages/node_modules/@node-red/editor-client/src/js/nodes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 366120796..d8c89665a 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -2357,13 +2357,13 @@ RED.nodes = (function() { node.type = "unknown"; } if (node._def.category != "config") { - if (n.hasOwnProperty('inputs') && def.defaults.hasOwnProperty("inputs")) { + if (n.hasOwnProperty('inputs') && node._def.defaults.hasOwnProperty("inputs")) { node.inputs = parseInt(n.inputs, 10); node._config.inputs = JSON.stringify(n.inputs); } else { node.inputs = node._def.inputs; } - if (n.hasOwnProperty('outputs') && def.defaults.hasOwnProperty("outputs")) { + if (n.hasOwnProperty('outputs') && node._def.defaults.hasOwnProperty("outputs")) { node.outputs = parseInt(n.outputs, 10); node._config.outputs = JSON.stringify(n.outputs); } else { From 100e5244c887695bdfd08f8d86cd2d2d95434ebd Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 17 Dec 2024 17:08:56 +0000 Subject: [PATCH 098/121] Bump for 4.0.7 --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- .../node_modules/@node-red/editor-api/package.json | 6 +++--- .../node_modules/@node-red/editor-client/package.json | 2 +- packages/node_modules/@node-red/nodes/package.json | 2 +- packages/node_modules/@node-red/registry/package.json | 4 ++-- packages/node_modules/@node-red/runtime/package.json | 6 +++--- packages/node_modules/@node-red/util/package.json | 2 +- packages/node_modules/node-red/package.json | 10 +++++----- 9 files changed, 25 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db473c61b..a85502524 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +#### 4.0.7: Maintenance Release + +Editor + + - Fix def can be undefined if the type is missing (#4997) @GogoVega + - Fix the user list of nested config node (#4995) @GogoVega + - Support custom login message and button (#4993) @knolleary + #### 4.0.6: Maintenance Release Editor diff --git a/package.json b/package.json index 679474fc8..dbddd1ea7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "4.0.6", + "version": "4.0.7", "description": "Low-code programming for event-driven applications", "homepage": "https://nodered.org", "license": "Apache-2.0", diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json index 14fdfb77a..741d8f350 100644 --- a/packages/node_modules/@node-red/editor-api/package.json +++ b/packages/node_modules/@node-red/editor-api/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-api", - "version": "4.0.6", + "version": "4.0.7", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/util": "4.0.6", - "@node-red/editor-client": "4.0.6", + "@node-red/util": "4.0.7", + "@node-red/editor-client": "4.0.7", "bcryptjs": "2.4.3", "body-parser": "1.20.3", "clone": "2.1.2", diff --git a/packages/node_modules/@node-red/editor-client/package.json b/packages/node_modules/@node-red/editor-client/package.json index 3d13bdc85..c51e7e759 100644 --- a/packages/node_modules/@node-red/editor-client/package.json +++ b/packages/node_modules/@node-red/editor-client/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-client", - "version": "4.0.6", + "version": "4.0.7", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json index 9659717fb..acb34f868 100644 --- a/packages/node_modules/@node-red/nodes/package.json +++ b/packages/node_modules/@node-red/nodes/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/nodes", - "version": "4.0.6", + "version": "4.0.7", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/registry/package.json b/packages/node_modules/@node-red/registry/package.json index 85058d0a7..8c6cb26f9 100644 --- a/packages/node_modules/@node-red/registry/package.json +++ b/packages/node_modules/@node-red/registry/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/registry", - "version": "4.0.6", + "version": "4.0.7", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,7 +16,7 @@ } ], "dependencies": { - "@node-red/util": "4.0.6", + "@node-red/util": "4.0.7", "clone": "2.1.2", "fs-extra": "11.2.0", "semver": "7.6.3", diff --git a/packages/node_modules/@node-red/runtime/package.json b/packages/node_modules/@node-red/runtime/package.json index 65ffc47a6..de0724263 100644 --- a/packages/node_modules/@node-red/runtime/package.json +++ b/packages/node_modules/@node-red/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/runtime", - "version": "4.0.6", + "version": "4.0.7", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/registry": "4.0.6", - "@node-red/util": "4.0.6", + "@node-red/registry": "4.0.7", + "@node-red/util": "4.0.7", "async-mutex": "0.5.0", "clone": "2.1.2", "express": "4.21.2", diff --git a/packages/node_modules/@node-red/util/package.json b/packages/node_modules/@node-red/util/package.json index 3338acd5f..596776a34 100644 --- a/packages/node_modules/@node-red/util/package.json +++ b/packages/node_modules/@node-red/util/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/util", - "version": "4.0.6", + "version": "4.0.7", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/node-red/package.json b/packages/node_modules/node-red/package.json index 35b93bc93..da21c410a 100644 --- a/packages/node_modules/node-red/package.json +++ b/packages/node_modules/node-red/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "4.0.6", + "version": "4.0.7", "description": "Low-code programming for event-driven applications", "homepage": "https://nodered.org", "license": "Apache-2.0", @@ -31,10 +31,10 @@ "flow" ], "dependencies": { - "@node-red/editor-api": "4.0.6", - "@node-red/runtime": "4.0.6", - "@node-red/util": "4.0.6", - "@node-red/nodes": "4.0.6", + "@node-red/editor-api": "4.0.7", + "@node-red/runtime": "4.0.7", + "@node-red/util": "4.0.7", + "@node-red/nodes": "4.0.7", "basic-auth": "2.0.1", "bcryptjs": "2.4.3", "cors": "2.8.5", From 7fa4e60c82d68f0f499df93a670f832be4c71540 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 20 Dec 2024 09:54:13 +0000 Subject: [PATCH 099/121] Fix config node sort order when importing --- .../@node-red/editor-client/src/js/nodes.js | 63 ++++++++++++++----- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 889cb0197..cab3547c2 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -2099,6 +2099,8 @@ RED.nodes = (function() { activeWorkspace = RED.workspaces.active(); } + const pendingConfigNodes = [] + const pendingConfigNodeIds = new Set() // Find all config nodes and add them for (i=0;iconfig node relationships are + // not very common + let iterationLimit = pendingConfigNodes.length * 5 + const handledConfigNodes = new Set() + while (pendingConfigNodes.length > 0 && iterationLimit > 0) { + const node = pendingConfigNodes.shift() + let hasPending = false + // Loop through the nodes referenced by this node to see if anything + // is pending + node._configNodeReferences.forEach(id => { + if (pendingConfigNodeIds.has(id) && !handledConfigNodes.has(id)) { + // This reference is for a node we know is in this import, but + // it isn't added yet - flag as pending + hasPending = true } + }) + if (!hasPending) { + // This node has no pending config node references - safe to add + delete node._configNodeReferences + new_nodes.push(node) + handledConfigNodes.add(node.id) + } else { + // This node has pending config node references + // Put to the back of the queue + pendingConfigNodes.push(node) } - return count; - }; - new_nodes.sort((a, b) => configNodeFilter(a) - configNodeFilter(b)); + iterationLimit-- + } + if (pendingConfigNodes.length > 0) { + // We exceeded the iteration count. Could be due to reference loops + // between the config nodes. At this point, just add the remaining + // nodes as-is + pendingConfigNodes.forEach(node => { + delete node._configNodeReferences + new_nodes.push(node) + }) + } // Find regular flow nodes and subflow instances for (i=0;i Date: Fri, 20 Dec 2024 11:06:08 +0000 Subject: [PATCH 100/121] Bump for 4.0.8 --- CHANGELOG.md | 6 ++++++ package.json | 2 +- .../node_modules/@node-red/editor-api/package.json | 6 +++--- .../node_modules/@node-red/editor-client/package.json | 2 +- packages/node_modules/@node-red/nodes/package.json | 2 +- packages/node_modules/@node-red/registry/package.json | 4 ++-- packages/node_modules/@node-red/runtime/package.json | 6 +++--- packages/node_modules/@node-red/util/package.json | 2 +- packages/node_modules/node-red/package.json | 10 +++++----- 9 files changed, 23 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a85502524..468cc1b1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ #### 4.0.7: Maintenance Release +Editor + + - Fix config node sort order when importing (#5000) @knolleary + +#### 4.0.7: Maintenance Release + Editor - Fix def can be undefined if the type is missing (#4997) @GogoVega diff --git a/package.json b/package.json index dbddd1ea7..185c73fe4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "4.0.7", + "version": "4.0.8", "description": "Low-code programming for event-driven applications", "homepage": "https://nodered.org", "license": "Apache-2.0", diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json index 741d8f350..f7a14b766 100644 --- a/packages/node_modules/@node-red/editor-api/package.json +++ b/packages/node_modules/@node-red/editor-api/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-api", - "version": "4.0.7", + "version": "4.0.8", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/util": "4.0.7", - "@node-red/editor-client": "4.0.7", + "@node-red/util": "4.0.8", + "@node-red/editor-client": "4.0.8", "bcryptjs": "2.4.3", "body-parser": "1.20.3", "clone": "2.1.2", diff --git a/packages/node_modules/@node-red/editor-client/package.json b/packages/node_modules/@node-red/editor-client/package.json index c51e7e759..60e9bb5e4 100644 --- a/packages/node_modules/@node-red/editor-client/package.json +++ b/packages/node_modules/@node-red/editor-client/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-client", - "version": "4.0.7", + "version": "4.0.8", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json index acb34f868..e9e9ef693 100644 --- a/packages/node_modules/@node-red/nodes/package.json +++ b/packages/node_modules/@node-red/nodes/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/nodes", - "version": "4.0.7", + "version": "4.0.8", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/registry/package.json b/packages/node_modules/@node-red/registry/package.json index 8c6cb26f9..d1d57a47f 100644 --- a/packages/node_modules/@node-red/registry/package.json +++ b/packages/node_modules/@node-red/registry/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/registry", - "version": "4.0.7", + "version": "4.0.8", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,7 +16,7 @@ } ], "dependencies": { - "@node-red/util": "4.0.7", + "@node-red/util": "4.0.8", "clone": "2.1.2", "fs-extra": "11.2.0", "semver": "7.6.3", diff --git a/packages/node_modules/@node-red/runtime/package.json b/packages/node_modules/@node-red/runtime/package.json index de0724263..1bef36e1b 100644 --- a/packages/node_modules/@node-red/runtime/package.json +++ b/packages/node_modules/@node-red/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/runtime", - "version": "4.0.7", + "version": "4.0.8", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/registry": "4.0.7", - "@node-red/util": "4.0.7", + "@node-red/registry": "4.0.8", + "@node-red/util": "4.0.8", "async-mutex": "0.5.0", "clone": "2.1.2", "express": "4.21.2", diff --git a/packages/node_modules/@node-red/util/package.json b/packages/node_modules/@node-red/util/package.json index 596776a34..9ee64e38b 100644 --- a/packages/node_modules/@node-red/util/package.json +++ b/packages/node_modules/@node-red/util/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/util", - "version": "4.0.7", + "version": "4.0.8", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/node-red/package.json b/packages/node_modules/node-red/package.json index da21c410a..ae352f223 100644 --- a/packages/node_modules/node-red/package.json +++ b/packages/node_modules/node-red/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "4.0.7", + "version": "4.0.8", "description": "Low-code programming for event-driven applications", "homepage": "https://nodered.org", "license": "Apache-2.0", @@ -31,10 +31,10 @@ "flow" ], "dependencies": { - "@node-red/editor-api": "4.0.7", - "@node-red/runtime": "4.0.7", - "@node-red/util": "4.0.7", - "@node-red/nodes": "4.0.7", + "@node-red/editor-api": "4.0.8", + "@node-red/runtime": "4.0.8", + "@node-red/util": "4.0.8", + "@node-red/nodes": "4.0.8", "basic-auth": "2.0.1", "bcryptjs": "2.4.3", "cors": "2.8.5", From 3da22882e9eb8281bdd50bd397857d3b74aa6b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathana=C3=ABl=20L=C3=A9caud=C3=A9?= Date: Tue, 7 Jan 2025 11:30:35 -0500 Subject: [PATCH 101/121] Fix typo in CHANGELOG (4.0.7-->4.0.8) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 468cc1b1c..d09c2acd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -#### 4.0.7: Maintenance Release +#### 4.0.8: Maintenance Release Editor From 953b7584a37d4c30a9bdc07e7d3d53525dea9427 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 13 Jan 2025 16:37:35 +0000 Subject: [PATCH 102/121] Avoid exceeding call stack when draining message group in Switch Fixes #5013 --- .../@node-red/nodes/core/function/10-switch.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/core/function/10-switch.js b/packages/node_modules/@node-red/nodes/core/function/10-switch.js index fdc345f47..9d68f6b0b 100644 --- a/packages/node_modules/@node-red/nodes/core/function/10-switch.js +++ b/packages/node_modules/@node-red/nodes/core/function/10-switch.js @@ -352,7 +352,9 @@ module.exports = function(RED) { if (msgs.length === 0) { done() } else { - drainMessageGroup(msgs,count,done); + setImmediate(() => { + drainMessageGroup(msgs,count,done); + }) } } }) @@ -505,7 +507,9 @@ module.exports = function(RED) { if (err) { node.error(err,nextMsg); } - processMessageQueue() + setImmediate(() => { + processMessageQueue() + }) }); } From 254b6a1e23445281f4578b2577d5ce36c247ec16 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Wed, 15 Jan 2025 10:32:10 +0000 Subject: [PATCH 103/121] Fix grunt dev via better ndoemon ignore rules --- .nodemonignore | 4 ---- nodemon.json | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) delete mode 100644 .nodemonignore create mode 100644 nodemon.json diff --git a/.nodemonignore b/.nodemonignore deleted file mode 100644 index 612a1e15b..000000000 --- a/.nodemonignore +++ /dev/null @@ -1,4 +0,0 @@ -/Gruntfile.js -/.git/* -*.backup -/public/* diff --git a/nodemon.json b/nodemon.json new file mode 100644 index 000000000..98d660626 --- /dev/null +++ b/nodemon.json @@ -0,0 +1,16 @@ +{ + "ignoreRoot": [ + ".git", + ".nyc_output", + ".sass-cache", + "bower-components", + "coverage" + ], + "ignore": [ + "/Gruntfile.js", + "/.git/*", + "*.backup", + "/public/*" + ] +} + From e2981f29702ba10d97da6d51a13bbf02ab8ac5bb Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 17 Jan 2025 16:45:44 +0000 Subject: [PATCH 104/121] Allow env var access to context --- .../@node-red/runtime/lib/flows/Flow.js | 8 ++++++++ .../@node-red/runtime/lib/flows/Group.js | 8 ++++++++ .../@node-red/runtime/lib/flows/util.js | 19 ++++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js index c4f4e39a2..0b6045326 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js @@ -719,6 +719,14 @@ class Flow { }); } + getContext(scope) { + if (scope === 'flow') { + return this.context + } else if (scope === 'global') { + return context.get('global') + } + } + dump() { console.log("==================") console.log(this.TYPE, this.id); diff --git a/packages/node_modules/@node-red/runtime/lib/flows/Group.js b/packages/node_modules/@node-red/runtime/lib/flows/Group.js index 589cdf115..d95b4e553 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/Group.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/Group.js @@ -49,6 +49,14 @@ class Group { } return this.parent.getSetting(key); } + + error(msg) { + this.parent.error(msg); + } + + getContext(scope) { + return this.parent.getContext(scope); + } } module.exports = { diff --git a/packages/node_modules/@node-red/runtime/lib/flows/util.js b/packages/node_modules/@node-red/runtime/lib/flows/util.js index 6b7f659b9..d50825212 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/util.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/util.js @@ -100,7 +100,24 @@ async function evaluateEnvProperties(flow, env, credentials) { } } else if (type ==='jsonata') { pendingEvaluations.push(new Promise((resolve, _) => { - redUtil.evaluateNodeProperty(value, 'jsonata', {_flow: flow}, null, (err, result) => { + redUtil.evaluateNodeProperty(value, 'jsonata',{ + // Fake a node object to provide access to _flow and context + _flow: flow, + context: () => { + return { + flow: { + get: (value, store, callback) => { + return flow.getContext('flow').get(value, store, callback) + } + }, + global: { + get: (value, store, callback) => { + return flow.getContext('global').get(value, store, callback) + } + } + } + } + }, null, (err, result) => { if (!err) { if (typeof result === 'object') { result = { value: result, __clone__: true} From 4cbf672b26679614b5ae2d4ef015621df0885def Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Fri, 17 Jan 2025 17:06:17 +0000 Subject: [PATCH 105/121] Fix library icon handling within library browser component Closes #5004 --- .../@node-red/editor-client/src/js/ui/library.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/library.js b/packages/node_modules/@node-red/editor-client/src/js/ui/library.js index 0098bc947..64b1f1547 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/library.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/library.js @@ -245,10 +245,15 @@ RED.library = (function() { if (lib.types && lib.types.indexOf(options.url) === -1) { return; } + let icon = 'fa fa-hdd-o'; + if (lib.icon) { + const fullIcon = RED.utils.separateIconPath(lib.icon); + icon = (fullIcon.module==="font-awesome"?"fa ":"")+fullIcon.file; + } listing.push({ library: lib.id, type: options.url, - icon: lib.icon || 'fa fa-hdd-o', + icon, label: RED._(lib.label||lib.id), path: "", expanded: true, @@ -303,10 +308,15 @@ RED.library = (function() { if (lib.types && lib.types.indexOf(options.url) === -1) { return; } + let icon = 'fa fa-hdd-o'; + if (lib.icon) { + const fullIcon = RED.utils.separateIconPath(lib.icon); + icon = (fullIcon.module==="font-awesome"?"fa ":"")+fullIcon.file; + } listing.push({ library: lib.id, type: options.url, - icon: lib.icon || 'fa fa-hdd-o', + icon, label: RED._(lib.label||lib.id), path: "", expanded: true, From 1acc16c9ef2fe203a0e630f51eed2d67f866e42b Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Sun, 19 Jan 2025 10:57:58 +0000 Subject: [PATCH 106/121] fix debug status reporting if null --- packages/node_modules/@node-red/nodes/core/common/21-debug.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/nodes/core/common/21-debug.js b/packages/node_modules/@node-red/nodes/core/common/21-debug.js index fe9827fce..45deb5ab3 100644 --- a/packages/node_modules/@node-red/nodes/core/common/21-debug.js +++ b/packages/node_modules/@node-red/nodes/core/common/21-debug.js @@ -148,7 +148,7 @@ module.exports = function(RED) { var st = (typeof output === 'string') ? output : util.inspect(output); var fill = "grey"; var shape = "dot"; - if (typeof output === 'object' && hasOwnProperty.call(output, "fill") && hasOwnProperty.call(output, "shape") && hasOwnProperty.call(output, "text")) { + if (typeof output === 'object' && output?.fill && output?.shape && output?.text) { fill = output.fill; shape = output.shape; st = output.text; From 13cac1b5efe9888c2a1bc922674f42ed9994af27 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 21 Jan 2025 13:56:44 +0000 Subject: [PATCH 107/121] Remember context sidebar tree state when refreshing Closes #5008 --- .../editor-client/src/js/ui/tab-context.js | 67 ++++++++++++------- .../editor-client/src/js/ui/utils.js | 59 +++++++++------- .../core/common/lib/debug/debug-utils.js | 3 +- 3 files changed, 79 insertions(+), 50 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js index 6cb034ccc..2c56786c8 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js @@ -18,8 +18,6 @@ RED.sidebar.context = (function() { var content; var sections; - var localCache = {}; - var flowAutoRefresh; var nodeAutoRefresh; var nodeSection; @@ -27,6 +25,8 @@ RED.sidebar.context = (function() { var flowSection; var globalSection; + const expandedPaths = {} + var currentNode; var currentFlow; @@ -212,14 +212,41 @@ RED.sidebar.context = (function() { var l = keys.length; for (var i = 0; i < l; i++) { sortedData[keys[i]].forEach(function(v) { - var k = keys[i]; - var l2 = sortedData[k].length; - var propRow = $('').appendTo(container); - var obj = $(propRow.children()[0]); + const k = keys[i]; + let payload = v.msg; + let format = v.format; + const tools = $(''); + expandedPaths[id + "." + k] = expandedPaths[id + "." + k] || new Set() + const objectElementOptions = { + typeHint: format, + sourceId: id + "." + k, + tools, + path: k, + rootPath: k, + exposeApi: true, + ontoggle: function(path,state) { + path = path.substring(k.length+1) + if (state) { + expandedPaths[id+"."+k].add(path) + } else { + // if 'a' has been collapsed, we want to remove 'a.b' and 'a[0]...' from the set + // of collapsed paths + for (let expandedPath of expandedPaths[id+"."+k]) { + if (expandedPath.startsWith(path+".") || expandedPath.startsWith(path+"[")) { + expandedPaths[id+"."+k].delete(expandedPath) + } + } + expandedPaths[id+"."+k].delete(path) + } + }, + expandPaths: [ ...expandedPaths[id+"."+k] ].sort(), + expandLeafNodes: true + } + const propRow = $('').appendTo(container); + const obj = $(propRow.children()[0]); obj.text(k); - var tools = $(''); const urlSafeK = encodeURIComponent(k) - var refreshItem = $('').appendTo(tools).on("click", function(e) { + const refreshItem = $('').appendTo(tools).on("click", function(e) { e.preventDefault(); e.stopPropagation(); $.getJSON(baseUrl+"/"+urlSafeK+"?store="+v.store, function(data) { @@ -229,16 +256,14 @@ RED.sidebar.context = (function() { tools.detach(); $(propRow.children()[1]).empty(); RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), { + ...objectElementOptions, typeHint: data.format, - sourceId: id+"."+k, - tools: tools, - path: k }).appendTo(propRow.children()[1]); } }) }); RED.popover.tooltip(refreshItem,RED._("sidebar.context.refrsh")); - var deleteItem = $('').appendTo(tools).on("click", function(e) { + const deleteItem = $('').appendTo(tools).on("click", function(e) { e.preventDefault(); e.stopPropagation(); var popover = RED.popover.create({ @@ -246,7 +271,7 @@ RED.sidebar.context = (function() { target: propRow, direction: "left", content: function() { - var content = $('
    '); + const content = $('
    '); $('

    ').appendTo(content); var row = $('

    ').appendTo(content); var bg = $('').appendTo(row); @@ -269,16 +294,15 @@ RED.sidebar.context = (function() { if (container.children().length === 0) { $('').appendTo(container).i18n(); } + delete expandedPaths[id + "." + k] } else { payload = data.msg; format = data.format; tools.detach(); $(propRow.children()[1]).empty(); RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), { - typeHint: data.format, - sourceId: id+"."+k, - tools: tools, - path: k + ...objectElementOptions, + typeHint: data.format }).appendTo(propRow.children()[1]); } }); @@ -293,14 +317,7 @@ RED.sidebar.context = (function() { }); RED.popover.tooltip(deleteItem,RED._("sidebar.context.delete")); - var payload = v.msg; - var format = v.format; - RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), { - typeHint: v.format, - sourceId: id+"."+k, - tools: tools, - path: k - }).appendTo(propRow.children()[1]); + RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), objectElementOptions).appendTo(propRow.children()[1]); if (contextStores.length > 1) { $("",{class:"red-ui-sidebar-context-property-storename"}).text(v.store).appendTo($(propRow.children()[0])) } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js index cfc7f65de..49ad15d87 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js @@ -230,7 +230,7 @@ RED.utils = (function() { var pinnedPaths = {}; var formattedPaths = {}; - function addMessageControls(obj,sourceId,key,msg,rootPath,strippedKey,extraTools) { + function addMessageControls(obj,sourceId,key,msg,rootPath,strippedKey,extraTools,enablePinning) { if (!pinnedPaths.hasOwnProperty(sourceId)) { pinnedPaths[sourceId] = {} } @@ -250,7 +250,7 @@ RED.utils = (function() { RED.clipboard.copyText(msg,copyPayload,"clipboard.copyMessageValue"); }) RED.popover.tooltip(copyPayload,RED._("node-red:debug.sidebar.copyPayload")); - if (strippedKey !== undefined && strippedKey !== '') { + if (enablePinning && strippedKey !== undefined && strippedKey !== '') { var isPinned = pinnedPaths[sourceId].hasOwnProperty(strippedKey); var pinPath = $('').appendTo(tools).on("click", function(e) { @@ -281,13 +281,16 @@ RED.utils = (function() { } } } - function checkExpanded(strippedKey,expandPaths,minRange,maxRange) { + function checkExpanded(strippedKey, expandPaths, { minRange, maxRange, expandLeafNodes }) { if (expandPaths && expandPaths.length > 0) { if (strippedKey === '' && minRange === undefined) { return true; } for (var i=0;i').appendTo(element); if (sourceId) { - addMessageControls(header,sourceId,path,obj,rootPath,strippedKey,tools); + addMessageControls(header,sourceId,path,obj,rootPath,strippedKey,tools, enablePinning); } if (!key) { element.addClass("red-ui-debug-msg-top-level"); - if (sourceId) { + if (sourceId && !expandPaths) { var pinned = pinnedPaths[sourceId]; expandPaths = []; if (pinned) { @@ -476,7 +481,7 @@ RED.utils = (function() { $('').text(typeHint||'string').appendTo(header); var row = $('

    ').appendTo(element); $('
    ').text(obj).appendTo(row);
    -                },function(state) {if (ontoggle) { ontoggle(path,state);}}, checkExpanded(strippedKey,expandPaths));
    +                },function(state) {if (ontoggle) { ontoggle(path,state);}}, checkExpanded(strippedKey, expandPaths, { expandLeafNodes }));
                 }
                 e = $('').html('"'+formatString(sanitize(obj))+'"').appendTo(entryObj);
                 if (/^#[0-9a-f]{6}$/i.test(obj)) {
    @@ -592,14 +597,16 @@ RED.utils = (function() {
                                         typeHint: type==='buffer'?'hex':false,
                                         hideKey: false,
                                         path: path+"["+i+"]",
    -                                    sourceId: sourceId,
    -                                    rootPath: rootPath,
    -                                    expandPaths: expandPaths,
    -                                    ontoggle: ontoggle,
    -                                    exposeApi: exposeApi,
    +                                    sourceId,
    +                                    rootPath,
    +                                    expandPaths,
    +                                    expandLeafNodes,
    +                                    ontoggle,
    +                                    exposeApi,
                                         // tools: tools // Do not pass tools down as we
                                                         // keep them attached to the top-level header
                                         nodeSelector: options.nodeSelector,
    +                                    enablePinning
                                     }
                                 ).appendTo(row);
                             }
    @@ -623,21 +630,23 @@ RED.utils = (function() {
                                                     typeHint: type==='buffer'?'hex':false,
                                                     hideKey: false,
                                                     path: path+"["+i+"]",
    -                                                sourceId: sourceId,
    -                                                rootPath: rootPath,
    -                                                expandPaths: expandPaths,
    -                                                ontoggle: ontoggle,
    -                                                exposeApi: exposeApi,
    +                                                sourceId,
    +                                                rootPath,
    +                                                expandPaths,
    +                                                expandLeafNodes,
    +                                                ontoggle,
    +                                                exposeApi,
                                                     // tools: tools // Do not pass tools down as we
                                                                     // keep them attached to the top-level header
                                                     nodeSelector: options.nodeSelector,
    +                                                enablePinning
                                                 }
                                             ).appendTo(row);
                                         }
                                     }
                                 })(),
                                 (function() { var path = path+"["+i+"]"; return function(state) {if (ontoggle) { ontoggle(path,state);}}})(),
    -                            checkExpanded(strippedKey,expandPaths,minRange,Math.min(fullLength-1,(minRange+9))));
    +                            checkExpanded(strippedKey,expandPaths,{ minRange, maxRange: Math.min(fullLength-1,(minRange+9)), expandLeafNodes}));
                                 $('').html("["+minRange+" … "+Math.min(fullLength-1,(minRange+9))+"]").appendTo(header);
                             }
                             if (fullLength < originalLength) {
    @@ -646,7 +655,7 @@ RED.utils = (function() {
                         }
                     },
                     function(state) {if (ontoggle) { ontoggle(path,state);}},
    -                checkExpanded(strippedKey,expandPaths));
    +                checkExpanded(strippedKey, expandPaths, { expandLeafNodes }));
                 }
             } else if (typeof obj === 'object') {
                 element.addClass('collapsed');
    @@ -680,14 +689,16 @@ RED.utils = (function() {
                                     typeHint: false,
                                     hideKey: false,
                                     path: newPath,
    -                                sourceId: sourceId,
    -                                rootPath: rootPath,
    -                                expandPaths: expandPaths,
    -                                ontoggle: ontoggle,
    -                                exposeApi: exposeApi,
    +                                sourceId,
    +                                rootPath,
    +                                expandPaths,
    +                                expandLeafNodes,
    +                                ontoggle,
    +                                exposeApi,
                                     // tools: tools // Do not pass tools down as we
                                                     // keep them attached to the top-level header
                                     nodeSelector: options.nodeSelector,
    +                                enablePinning
                                 }
                             ).appendTo(row);
                         }
    @@ -696,7 +707,7 @@ RED.utils = (function() {
                         }
                     },
                     function(state) {if (ontoggle) { ontoggle(path,state);}},
    -                checkExpanded(strippedKey,expandPaths));
    +                checkExpanded(strippedKey, expandPaths, { expandLeafNodes }));
                 }
                 if (key) {
                     $('').text(type).appendTo(entryObj);
    diff --git a/packages/node_modules/@node-red/nodes/core/common/lib/debug/debug-utils.js b/packages/node_modules/@node-red/nodes/core/common/lib/debug/debug-utils.js
    index b244273dc..0a84b24c7 100644
    --- a/packages/node_modules/@node-red/nodes/core/common/lib/debug/debug-utils.js
    +++ b/packages/node_modules/@node-red/nodes/core/common/lib/debug/debug-utils.js
    @@ -511,9 +511,10 @@ RED.debug = (function() {
                 typeHint: format,
                 hideKey: false,
                 path: path,
    -            sourceId: sourceNode&&sourceNode.id,
    +            sourceId: sourceNode && sourceNode.id,
                 rootPath: path,
                 nodeSelector: config.messageSourceClick,
    +            enablePinning: true
             });
             // Do this in a separate step so the element functions aren't stripped
             debugMessage.appendTo(el);
    
    From 48d2d269a5ede2882d1677596da6497316d3b756 Mon Sep 17 00:00:00 2001
    From: Nick O'Leary 
    Date: Tue, 21 Jan 2025 16:15:13 +0000
    Subject: [PATCH 108/121] Do not select group when triggering quick-add within
     it
    
    ---
     .../@node-red/editor-client/src/js/ui/view.js             | 8 --------
     1 file changed, 8 deletions(-)
    
    diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js
    index 8ce6dc630..92d4593c6 100644
    --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js
    +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js
    @@ -1265,11 +1265,6 @@ RED.view = (function() {
             var targetGroup = options.group;
             var touchTrigger = options.touchTrigger;
     
    -        if (targetGroup) {
    -            selectedGroups.add(targetGroup,false);
    -            RED.view.redraw();
    -        }
    -
             // `point` is the place in the workspace the mouse has clicked.
             //  This takes into account scrolling and scaling of the workspace.
             var ox = point[0];
    @@ -1591,9 +1586,6 @@ RED.view = (function() {
                     // auto select dropped node - so info shows (if visible)
                     clearSelection();
                     nn.selected = true;
    -                if (targetGroup) {
    -                    selectedGroups.add(targetGroup,false);
    -                }
                     movingSet.add(nn);
                     updateActiveNodes();
                     updateSelection();
    
    From daa76e6e5fe6c68d835b0c2bf71cdec2916ee05a Mon Sep 17 00:00:00 2001
    From: Nick O'Leary 
    Date: Wed, 22 Jan 2025 10:25:24 +0000
    Subject: [PATCH 109/121] Update sf instance env vars when removed from
     template
    
    ---
     .../editor-client/src/js/ui/editor.js         | 49 +++++++++++++++++--
     1 file changed, 44 insertions(+), 5 deletions(-)
    
    diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js
    index ca74b3eff..f2e40147d 100644
    --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js
    +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js
    @@ -1836,8 +1836,18 @@ RED.editor = (function() {
                                     }
                                 });
                             }
    -
    +                        let envToRemove = new Set()
                             if (!isSameObj(old_env, new_env)) {
    +                            // Get a list of env properties that have been removed
    +                            // by comparing old_env and new_env
    +                            if (old_env) {
    +                                old_env.forEach(env => { envToRemove.add(env.name) })
    +                            }
    +                            if (new_env) {
    +                                new_env.forEach(env => {
    +                                    envToRemove.delete(env.name)
    +                                })
    +                            }
                                 editState.changes.env = editing_node.env;
                                 editing_node.env = new_env;
                                 editState.changed = true;
    @@ -1846,10 +1856,11 @@ RED.editor = (function() {
     
     
                             if (editState.changed) {
    -                            var wasChanged = editing_node.changed;
    +                            let wasChanged = editing_node.changed;
                                 editing_node.changed = true;
                                 validateNode(editing_node);
    -                            var subflowInstances = [];
    +                            let subflowInstances = [];
    +                            let instanceHistoryEvents = []
                                 RED.nodes.eachNode(function(n) {
                                     if (n.type == "subflow:"+editing_node.id) {
                                         subflowInstances.push({
    @@ -1859,13 +1870,35 @@ RED.editor = (function() {
                                         n._def.color = editing_node.color;
                                         n.changed = true;
                                         n.dirty = true;
    +                                    if (n.env) {
    +                                        const oldEnv = n.env
    +                                        const newEnv = []
    +                                        let envChanged = false
    +                                        n.env.forEach((env, index) => {
    +                                            if (envToRemove.has(env.name)) {
    +                                                envChanged = true
    +                                            } else {
    +                                                newEnv.push(env)
    +                                            }
    +                                        })
    +                                        if (envChanged) {
    +                                            instanceHistoryEvents.push({
    +                                                t: 'edit',
    +                                                node: n,
    +                                                changes: { env: oldEnv },
    +                                                dirty: n.dirty,
    +                                                changed: n.changed
    +                                            })
    +                                            n.env = newEnv
    +                                        }
    +                                    }
                                         updateNodeProperties(n);
                                         validateNode(n);
                                     }
                                 });
                                 RED.events.emit("subflows:change",editing_node);
                                 RED.nodes.dirty(true);
    -                            var historyEvent = {
    +                            let historyEvent = {
                                     t:'edit',
                                     node:editing_node,
                                     changes:editState.changes,
    @@ -1875,7 +1908,13 @@ RED.editor = (function() {
                                         instances:subflowInstances
                                     }
                                 };
    -
    +                            if (instanceHistoryEvents.length > 0) {
    +                                historyEvent = {
    +                                    t: 'multi',
    +                                    events: [ historyEvent, ...instanceHistoryEvents ],
    +                                    dirty: wasDirty
    +                                }
    +                            }
                                 RED.history.push(historyEvent);
                             }
                             editing_node.dirty = true;
    
    From 43df2318d49de52e2b15ba32aed0af101ecbfa80 Mon Sep 17 00:00:00 2001
    From: Nick O'Leary 
    Date: Mon, 27 Jan 2025 16:36:57 +0000
    Subject: [PATCH 110/121] Show subflow flow context under node section of
     sidebar
    
    ---
     packages/node_modules/@node-red/runtime/lib/api/context.js | 6 +++++-
     1 file changed, 5 insertions(+), 1 deletion(-)
    
    diff --git a/packages/node_modules/@node-red/runtime/lib/api/context.js b/packages/node_modules/@node-red/runtime/lib/api/context.js
    index f27075577..a5612997a 100644
    --- a/packages/node_modules/@node-red/runtime/lib/api/context.js
    +++ b/packages/node_modules/@node-red/runtime/lib/api/context.js
    @@ -96,7 +96,11 @@ var api = module.exports = {
                 } else if (scope === 'node') {
                     var node = runtime.nodes.getNode(id);
                     if (node) {
    -                    ctx = node.context();
    +                    if (/^subflow:/.test(node.type)) {
    +                        ctx = runtime.nodes.getContext(node.id);
    +                    } else {
    +                        ctx = node.context();
    +                    }
                     }
                 }
                 if (ctx) {
    
    From ffdbd94927887a721f6ccd785a1af1d8c47aaf9f Mon Sep 17 00:00:00 2001
    From: Nick O'Leary 
    Date: Wed, 29 Jan 2025 14:22:44 +0000
    Subject: [PATCH 111/121] Handle dragging node into group and splicing link at
     same time Fixes #5026
    
    ---
     .../@node-red/editor-client/src/js/ui/view.js | 19 ++++++++++++-------
     1 file changed, 12 insertions(+), 7 deletions(-)
    
    diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js
    index 92d4593c6..198af0850 100644
    --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js
    +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js
    @@ -2170,19 +2170,24 @@ RED.view = (function() {
                             n.n.moved = true;
                         }
                     }
    -
    -                // Check to see if we need to splice a link
    +                // If a node has moved and ends up being spliced into a link, keep
    +                // track of which historyEvent to add the splice info to
    +                let targetSpliceEvent = null
                     if (moveEvent.nodes.length > 0) {
                         historyEvent.events.push(moveEvent)
    -                    if (activeSpliceLink) {
    -                        var linkToSplice = d3.select(activeSpliceLink).data()[0];
    -                        spliceLink(linkToSplice, movingSet.get(0).n, moveEvent)
    -                    }
    +                    targetSpliceEvent = moveEvent
                     }
                     if (moveAndChangedGroupEvent.nodes.length > 0) {
                         historyEvent.events.push(moveAndChangedGroupEvent)
    +                    targetSpliceEvent = moveAndChangedGroupEvent
                     }
    -                
    +                // activeSpliceLink will only be set if the movingSet has a single
    +                // node that is able to splice.
    +                if (targetSpliceEvent && activeSpliceLink) {
    +                    var linkToSplice = d3.select(activeSpliceLink).data()[0];
    +                    spliceLink(linkToSplice, movingSet.get(0).n, targetSpliceEvent)
    +                }
    +
                     // Only continue if something has moved
                     if (historyEvent.events.length > 0) {
                         RED.nodes.dirty(true);
    
    From ca61efc98698480e4e938295d7a876cb1a58dc86 Mon Sep 17 00:00:00 2001
    From: matiseni51 
    Date: Sat, 1 Feb 2025 11:44:57 +0100
    Subject: [PATCH 112/121] feat: Add custom label for default deploy button in
     settings.editorTheme
    
    ---
     .../node_modules/@node-red/editor-client/src/js/ui/deploy.js | 5 +++--
     1 file changed, 3 insertions(+), 2 deletions(-)
    
    diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js b/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js
    index ed503bcfe..d77a4dba2 100644
    --- a/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js
    +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js
    @@ -44,6 +44,7 @@ RED.deploy = (function() {
         /**
          * options:
          *   type: "default" - Button with drop-down options - no further customisation available
    +     *      label: the text to display - default: "Deploy"
          *   type: "simple"  - Button without dropdown. Customisations:
          *      label: the text to display - default: "Deploy"
          *      icon : the icon to use. Null removes the icon. default: "red/images/deploy-full-o.svg"
    @@ -51,13 +52,14 @@ RED.deploy = (function() {
         function init(options) {
             options = options || {};
             var type = options.type || "default";
    +        var label = options.label || RED._("deploy.deploy");
     
             if (type == "default") {
                 $('
  • '+ ''+ ''+ ' '+ - ''+RED._("deploy.deploy")+''+ + ''+label+''+ ''+ ''+ ''+ @@ -78,7 +80,6 @@ RED.deploy = (function() { mainMenuItems.push({id:"deploymenu-item-reload", icon:"red/images/deploy-reload.svg",label:RED._("deploy.restartFlows"),sublabel:RED._("deploy.restartFlowsDesc"),onselect:"core:restart-flows"}) RED.menu.init({id:"red-ui-header-button-deploy-options", options: mainMenuItems }); } else if (type == "simple") { - var label = options.label || RED._("deploy.deploy"); var icon = 'red/images/deploy-full-o.svg'; if (options.hasOwnProperty('icon')) { icon = options.icon; From 7d9e09f5a7abad231a8f0e2bc86690e56cfc5ad9 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 10 Feb 2025 16:23:13 +0000 Subject: [PATCH 113/121] Handle long auto-complete suggests Fixes #5028 --- .../src/js/ui/common/autoComplete.js | 2 +- .../src/js/ui/common/typedInput.js | 42 ++++++++++++------- .../src/sass/ui/common/autoComplete.scss | 11 +++++ .../@node-red/runtime/lib/api/context.js | 20 +++++++-- 4 files changed, 56 insertions(+), 19 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js index a3ce0bcd1..a4de3f2a9 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js @@ -61,7 +61,7 @@ } this.menu = RED.popover.menu({ tabSelect: true, - width: 300, + width: Math.max(300, this.element.width()), maxHeight: 200, class: "red-ui-autoComplete-container", options: completions, diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js index 47355565f..22e509d08 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js @@ -63,6 +63,7 @@ pre: value.substring(0,idx), match: value.substring(idx,idx+len), post: value.substring(idx+len), + exact: idx === 0 && value.length === searchValue.length } } function generateSpans(match) { @@ -83,7 +84,7 @@ const srcMatch = getMatch(optSrc, val); if (valMatch.found || srcMatch.found) { const element = $('
    ',{style: "display: flex"}); - const valEl = $('
    ',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"}); + const valEl = $('
    ',{ class: "red-ui-autoComplete-completion" }); valEl.append(generateSpans(valMatch)); valEl.appendTo(element); if (optSrc) { @@ -159,7 +160,7 @@ if (valMatch.found) { const optSrc = envVarsMap[v] const element = $('
    ',{style: "display: flex"}); - const valEl = $('
    ',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"}); + const valEl = $('
    ',{ class: "red-ui-autoComplete-completion" }); valEl.append(generateSpans(valMatch)) valEl.appendTo(element) @@ -201,7 +202,7 @@ const that = this const getContextKeysFromRuntime = function(scope, store, searchKey, done) { contextKnownKeys[scope] = contextKnownKeys[scope] || {} - contextKnownKeys[scope][store] = contextKnownKeys[scope][store] || new Set() + contextKnownKeys[scope][store] = contextKnownKeys[scope][store] || new Map() if (searchKey.length > 0) { try { RED.utils.normalisePropertyExpression(searchKey) @@ -223,11 +224,12 @@ const result = data[store] || {} const keys = result.keys || [] const keyPrefix = searchKey + (searchKey.length > 0 ? '.' : '') - keys.forEach(key => { + keys.forEach(keyInfo => { + const key = keyInfo.key if (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(key)) { - contextKnownKeys[scope][store].add(keyPrefix + key) + contextKnownKeys[scope][store].set(keyPrefix + key, keyInfo) } else { - contextKnownKeys[scope][store].add(searchKey + "[\""+key.replace(/"/,"\\\"")+"\"]") + contextKnownKeys[scope][store].set(searchKey + "[\""+key.replace(/"/,"\\\"")+"\"]", keyInfo) } }) done() @@ -242,14 +244,14 @@ // Get the flow id of the node we're editing const editStack = RED.editor.getEditStack() if (editStack.length === 0) { - done([]) + done(new Map()) return } const editingNode = editStack.pop() if (editingNode.z) { scope = `${scope}/${editingNode.z}` } else { - done([]) + done(new Map()) return } } @@ -269,17 +271,29 @@ return function(val, done) { getContextKeys(val, function (keys) { const matches = [] - keys.forEach(v => { + keys.forEach((keyInfo, v) => { let optVal = v let valMatch = getMatch(optVal, val); - if (!valMatch.found && val.length > 0 && val.endsWith('.')) { - // Search key ends in '.' - but doesn't match. Check again - // with [" at the end instead so we match bracket notation - valMatch = getMatch(optVal, val.substring(0, val.length - 1) + '["') + if (!valMatch.found && val.length > 0) { + if (val.endsWith('.')) { + // Search key ends in '.' - but doesn't match. Check again + // with [" at the end instead so we match bracket notation + valMatch = getMatch(optVal, val.substring(0, val.length - 1) + '["') + // } else if (val.endsWith('[') && /^array/.test(keyInfo.format)) { + // console.log('this case') + } } if (valMatch.found) { const element = $('
    ',{style: "display: flex"}); - const valEl = $('
    ',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"}); + const valEl = $('
    ',{ class: "red-ui-autoComplete-completion" }); + // if (keyInfo.format) { + // valMatch.post += ' ' + keyInfo.format + // } + if (valMatch.exact && /^array/.test(keyInfo.format)) { + valMatch.post += `[0-${keyInfo.length}]` + optVal += '[' + + } valEl.append(generateSpans(valMatch)) valEl.appendTo(element) matches.push({ diff --git a/packages/node_modules/@node-red/editor-client/src/sass/ui/common/autoComplete.scss b/packages/node_modules/@node-red/editor-client/src/sass/ui/common/autoComplete.scss index 0501bb6a2..85cb4f1db 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/ui/common/autoComplete.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/ui/common/autoComplete.scss @@ -2,4 +2,15 @@ &.red-ui-popover-panel { border-top: none; } + + +} +.red-ui-autoComplete-completion { + font-family: var(--red-ui-monospace-font); + white-space: nowrap; + overflow: hidden; + flex-grow: 1; + text-overflow: ellipsis; + direction: rtl; + text-align: left; } diff --git a/packages/node_modules/@node-red/runtime/lib/api/context.js b/packages/node_modules/@node-red/runtime/lib/api/context.js index f27075577..c13beb9f6 100644 --- a/packages/node_modules/@node-red/runtime/lib/api/context.js +++ b/packages/node_modules/@node-red/runtime/lib/api/context.js @@ -104,13 +104,25 @@ var api = module.exports = { store = store || availableStores.default; ctx.get(key,store,function(err, v) { if (opts.keysOnly) { + const result = {} if (Array.isArray(v)) { - resolve({ [store]: { format: `array[${v.length}]`}}) + result.format = `array[${v.length}]` } else if (typeof v === 'object') { - resolve({ [store]: { keys: Object.keys(v), format: 'Object' } }) + result.keys = Object.keys(v).map(k => { + if (Array.isArray(v[k])) { + return { key: k, format: `array[${v[k].length}]`, length: v[k].length } + } else if (typeof v[k] === 'object') { + return { key: k, format: 'object' } + } else { + return { key: k } + } + }) + result.format = 'object' } else { - resolve({ [store]: { keys: [] }}) + result.keys = [] } + resolve({ [store]: result }) + return } var encoded = util.encodeObject({msg:v}); if (store !== availableStores.default) { @@ -147,7 +159,7 @@ var api = module.exports = { } return } - result[store] = { keys } + result[store] = { keys: keys.map(key => { return { key }}) } c--; if (c === 0) { if (!errorReported) { From 77c4ccf8fbce63724abe6ec3c80236bba73c9ae5 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Mon, 10 Feb 2025 16:41:18 +0000 Subject: [PATCH 114/121] Handle undefined username when generating user icon Fixes #5036 --- .../@node-red/editor-client/src/js/multiplayer.js | 7 +++---- .../node_modules/@node-red/editor-client/src/js/user.js | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/multiplayer.js b/packages/node_modules/@node-red/editor-client/src/js/multiplayer.js index b37d90fcb..fed5f518a 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/multiplayer.js +++ b/packages/node_modules/@node-red/editor-client/src/js/multiplayer.js @@ -398,14 +398,13 @@ RED.multiplayer = (function () { anonIconBody.setAttribute("d",`M ${radius/2} ${radius/2 + 5} h -2.5 c -2 1 -2 -5 0.5 -4.5 c 2 1 2 1 4 0 c 2.5 -0.5 2.5 5.5 0 4.5 z`); group.appendChild(anonIconBody) } else { - const labelText = user.username ? user.username.substring(0,2) : user const label = document.createElementNS("http://www.w3.org/2000/svg","text"); - if (user.username) { + if (user.username || user.email) { label.setAttribute("class","red-ui-multiplayer-annotation-label"); - label.textContent = user.username.substring(0,2) + label.textContent = (user.username || user.email).substring(0,2) } else { label.setAttribute("class","red-ui-multiplayer-annotation-label red-ui-multiplayer-user-count") - label.textContent = user + label.textContent = 'nr' } label.setAttribute("text-anchor", "middle") label.setAttribute("x",radius/2); diff --git a/packages/node_modules/@node-red/editor-client/src/js/user.js b/packages/node_modules/@node-red/editor-client/src/js/user.js index e2c3cb577..e626f4ec0 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/user.js +++ b/packages/node_modules/@node-red/editor-client/src/js/user.js @@ -351,10 +351,10 @@ RED.user = (function() { userIcon.css({ backgroundImage: "url("+user.image+")", }) - } else if (user.anonymous) { + } else if (user.anonymous || (!user.username && !user.email)) { $('').appendTo(userIcon); } else { - $('').text(user.username.substring(0,2)).appendTo(userIcon); + $('').text((user.username || user.email).substring(0,2)).appendTo(userIcon); } if (user.profileColor !== undefined) { userIcon.addClass('red-ui-user-profile-color-' + user.profileColor) From 4984af48f135f71e27266a677a4650b983d2a55c Mon Sep 17 00:00:00 2001 From: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:42:55 +0000 Subject: [PATCH 115/121] Update monaco.js for symbol error --- .../editor-client/src/js/ui/editors/code-editors/monaco.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/monaco.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/monaco.js index b9f586944..b35e60d50 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/monaco.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/monaco.js @@ -691,6 +691,7 @@ RED.editor.codeEditor.monaco = (function() { 2322, //Type 'unknown' is not assignable to type 'string' 2339, //property does not exist on 2345, //Argument of type xxx is not assignable to parameter of type 'DateTimeFormatOptions' + 2538, //Ignore symbols as index property error. 7043, //i forget what this one is, 80001, //Convert to ES6 module 80004, //JSDoc types may be moved to TypeScript types. From 27463197cdaf4cb8552e5646189e0ca128898ed9 Mon Sep 17 00:00:00 2001 From: aikitori Date: Thu, 13 Feb 2025 08:55:10 +0100 Subject: [PATCH 116/121] Add details for the dynamic subscription to match the English documentation --- .../nodes/locales/de/network/10-mqtt.html | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/packages/node_modules/@node-red/nodes/locales/de/network/10-mqtt.html b/packages/node_modules/@node-red/nodes/locales/de/network/10-mqtt.html index 2aa31c081..e5c7b5fb5 100644 --- a/packages/node_modules/@node-red/nodes/locales/de/network/10-mqtt.html +++ b/packages/node_modules/@node-red/nodes/locales/de/network/10-mqtt.html @@ -39,10 +39,36 @@
    MQTTv5: Ablaufzeit der Nachricht in Sekunden.

Details

-

Das abonnierte Topic darf MQTT-Platzhalterzeichen (wildcards) enthalten (+ für eine Ebene und # für mehrere Ebenen).

-

Dieser Node erfordert eine Verbindung zu einem MQTT-Broker, der über die Auswahlliste selektiert werden kann. - Eine neue Verbindung wird durch Klicken auf das Stiftsymbol erstellt.

+

Das abonnierte Topic darf MQTT-Platzhalterzeichen (wildcards) enthalten (+ für eine Ebene und # für mehrere Ebenen).

+

Diese Node erfordert eine Verbindung zu einem MQTT-Broker, der über die Auswahlliste selektiert werden kann. Eine neue Verbindung wird durch Klicken auf das Stiftsymbol erstellt.

Mehrere MQTT-Nodes (in oder out) können bei Bedarf dieselbe Broker-Verbindung nutzen.

+

Dynamische Steuerung

+ Die von der Node genutzte Verbindung kann dynamisch gesteuert werden, wenn die MQTT-Node eine der folgenden Nachrichten erhält. Die Payload dieser Nachrichten werden nicht veröffentlicht. +

Eingangsdaten

+

Nur Verfügbar, wenn die Node für dynamische Abonnements konfiguriert wurde.

+
+
action string
+
Der Name der Aktion, die die MQTT-Node ausführen soll. Verfügbare Aktionen sind: "connect", "disconnect", "getSubscriptions", "subscribe" und "unsubscribe".
+
topic string|object|array
+
Bei den Aktionen "subscribe" und "unsubscribe" gibt diese Eigenschaft die MQTT-Topic an. Dabei kann es sich um Folgendes handeln: +
    +
  • eine Zeichenfolge, die den Topic-Filter enthält
  • +
  • ein Objekt mit den Eigenschaften topic und qos
  • +
  • ein Array aus Zeichenfolgen oder Objekten, um mehrere Topics gleichzeitig zu verwalten
  • +
+
+
broker broker
+
Für die Aktion "connect" kann diese Eigenschaft jede der einzelnen Broker-Konfigurationseinstellungen überschreiben, einschließlich:
    +
  • broker
  • +
  • port
  • +
  • url - überschreibt Broker/Port, um eine vollständige Verbindungs-URL bereitzustellen
  • +
  • username
  • +
  • password
  • +
+

Wenn diese Eigenschaft gesetzt ist und der Broker bereits verbunden ist, wird ein Fehler protokolliert, es sei denn, die Eigenschaft force gesetzt - in diesem Fall wird die Verbindung zum Broker getrennt, die neuen Einstellungen angewendet und erneut verbunden.

+
+
+