diff --git a/package.json b/package.json index 561609dcf..d58680afd 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.18.2", + "express": "4.19.2", "express-session": "1.17.3", "form-data": "4.0.0", "fs-extra": "11.1.1", @@ -64,7 +64,7 @@ "mqtt": "4.3.7", "multer": "1.4.5-lts.1", "mustache": "4.2.0", - "node-red-admin": "^3.1.2", + "node-red-admin": "^3.1.3", "node-watch": "0.7.4", "nopt": "5.0.0", "oauth2orize": "1.11.1", @@ -112,7 +112,7 @@ "mermaid": "^10.4.0", "minami": "1.2.3", "mocha": "9.2.2", - "node-red-node-test-helper": "^0.3.2", + "node-red-node-test-helper": "^0.3.3", "nodemon": "2.0.20", "proxy": "^1.0.2", "sass": "1.62.1", diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json index fdb00cab9..d6ea03a76 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.17.3", - "express": "4.18.2", + "express": "4.19.2", "memorystore": "1.6.7", "mime": "3.0.0", "multer": "1.4.5-lts.1", 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 8d0ae79b3..40d19a811 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 @@ -574,12 +574,16 @@ RED.nodes = (function() { * @param {String} z tab id */ checkTabState: function (z) { - const ws = workspaces[z] + const ws = workspaces[z] || subflows[z] if (ws) { const contentsChanged = tabDirtyMap[z].size > 0 || tabDeletedNodesMap[z].size > 0 if (Boolean(ws.contentsChanged) !== contentsChanged) { ws.contentsChanged = contentsChanged - RED.events.emit("flows:change", ws); + if (ws.type === 'tab') { + RED.events.emit("flows:change", ws); + } else { + RED.events.emit("subflows:change", ws); + } } } } @@ -1052,7 +1056,22 @@ RED.nodes = (function() { RED.nodes.registerType("subflow:"+sf.id, { defaults:{ name:{value:""}, - env:{value:[]} + env:{value:[], validate: function(value) { + const errors = [] + if (value) { + value.forEach(env => { + const r = RED.utils.validateTypedProperty(env.value, env.type) + if (r !== true) { + errors.push(env.name+': '+r) + } + }) + } + if (errors.length === 0) { + return true + } else { + return errors + } + }} }, icon: function() { return sf.icon||"subflow.svg" }, category: sf.category || "subflows", 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 093e7995f..dae3f6fa6 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 @@ -118,10 +118,16 @@ RED.contextMenu = (function () { onselect: 'core:split-wire-with-link-nodes', disabled: !canEdit || !hasLinks }, - null, - { onselect: 'core:show-import-dialog', label: RED._('common.label.import')}, - { onselect: 'core:show-examples-import-dialog', label: RED._('menu.label.importExample') } + null ) + if (RED.settings.theme("menu.menu-item-import-library", true)) { + insertOptions.push( + { onselect: 'core:show-import-dialog', label: RED._('common.label.import')}, + { onselect: 'core:show-examples-import-dialog', label: RED._('menu.label.importExample') } + ) + } + + if (hasSelection && canEdit) { const nodeOptions = [] if (!hasMultipleSelection && !isGroup) { @@ -194,8 +200,14 @@ RED.contextMenu = (function () { { onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !canEdit || !RED.view.clipboard() }, { onselect: 'core:delete-selection', label: RED._('keyboard.deleteSelected'), disabled: !canEdit || !canDelete }, { onselect: 'core:delete-selection-and-reconnect', label: RED._('keyboard.deleteReconnect'), disabled: !canEdit || !canDelete }, - { onselect: 'core:show-export-dialog', label: RED._("menu.label.export") }, - { onselect: 'core:select-all-nodes', label: RED._("keyboard.selectAll") }, + ) + if (RED.settings.theme("menu.menu-item-export-library", true)) { + menuItems.push( + { onselect: 'core:show-export-dialog', label: RED._("menu.label.export") } + ) + } + menuItems.push( + { onselect: 'core:select-all-nodes', label: RED._("keyboard.selectAll") } ) } 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 2fa78f679..57a9de470 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 @@ -491,6 +491,11 @@ RED.workspaces = (function() { createWorkspaceTabs(); RED.events.on("sidebar:resize",workspace_tabs.resize); + RED.events.on("workspace:clear", () => { + // Reset the index used to generate new flow names + workspaceIndex = 0 + }) + RED.actions.add("core:show-next-tab",function() { var oldActive = activeWorkspace; workspace_tabs.nextTab(); @@ -657,6 +662,9 @@ RED.workspaces = (function() { RED.events.on("flows:change", (ws) => { $("#red-ui-tab-"+(ws.id.replace(".","-"))).toggleClass('red-ui-workspace-changed',!!(ws.contentsChanged || ws.changed || ws.added)); }) + RED.events.on("subflows:change", (ws) => { + $("#red-ui-tab-"+(ws.id.replace(".","-"))).toggleClass('red-ui-workspace-changed',!!(ws.contentsChanged || ws.changed || ws.added)); + }) hideWorkspace(); } 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 76dbe2223..e753075b5 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/util.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/util.js @@ -106,14 +106,22 @@ async function evaluateEnvProperties(flow, env, credentials) { result = { value: result, __clone__: true} } evaluatedEnv[name] = result + } else { + evaluatedEnv[name] = undefined + flow.error(`Error evaluating env property '${name}': ${err.toString()}`) } resolve() }); })) } else { - value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null); - if (typeof value === 'object') { - value = { value: value, __clone__: true} + try { + value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null); + if (typeof value === 'object') { + value = { value: value, __clone__: true} + } + } catch (err) { + value = undefined + flow.error(`Error evaluating env property '${name}': ${err.toString()}`) } } evaluatedEnv[name] = value diff --git a/packages/node_modules/@node-red/runtime/package.json b/packages/node_modules/@node-red/runtime/package.json index 0dc566aec..d9d5c5b83 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.0-beta.1", "async-mutex": "0.4.0", "clone": "2.1.2", - "express": "4.18.2", + "express": "4.19.2", "fs-extra": "11.1.1", "json-stringify-safe": "5.0.1" } diff --git a/packages/node_modules/node-red/package.json b/packages/node_modules/node-red/package.json index f2b5ffe91..a94184448 100644 --- a/packages/node_modules/node-red/package.json +++ b/packages/node_modules/node-red/package.json @@ -37,9 +37,9 @@ "@node-red/nodes": "4.0.0-beta.1", "basic-auth": "2.0.1", "bcryptjs": "2.4.3", - "express": "4.18.2", + "express": "4.19.2", "fs-extra": "11.1.1", - "node-red-admin": "^3.1.2", + "node-red-admin": "^3.1.3", "nopt": "5.0.0", "semver": "7.5.4" }, diff --git a/test/nodes/core/network/21-httprequest_spec.js b/test/nodes/core/network/21-httprequest_spec.js index 4d159e402..6459a2122 100644 --- a/test/nodes/core/network/21-httprequest_spec.js +++ b/test/nodes/core/network/21-httprequest_spec.js @@ -60,6 +60,7 @@ describe('HTTP Request Node', function() { function startServer(done) { testPort += 1; testServer = stoppable(http.createServer(testApp)); + const promises = [] testServer.listen(testPort,function(err) { testSslPort += 1; console.log("ssl port", testSslPort); @@ -81,13 +82,17 @@ describe('HTTP Request Node', function() { */ }; testSslServer = stoppable(https.createServer(sslOptions,testApp)); - testSslServer.listen(testSslPort, function(err){ - if (err) { - console.log(err); - } else { - console.log("started testSslServer"); - } - }); + console.log('> start testSslServer') + promises.push(new Promise((resolve, reject) => { + testSslServer.listen(testSslPort, function(err){ + console.log(' done testSslServer') + if (err) { + reject(err) + } else { + resolve() + } + }); + })) testSslClientPort += 1; var sslClientOptions = { @@ -97,10 +102,17 @@ describe('HTTP Request Node', function() { requestCert: true }; testSslClientServer = stoppable(https.createServer(sslClientOptions, testApp)); - testSslClientServer.listen(testSslClientPort, function(err){ - console.log("ssl-client", err) - }); - + console.log('> start testSslClientServer') + promises.push(new Promise((resolve, reject) => { + testSslClientServer.listen(testSslClientPort, function(err){ + console.log(' done testSslClientServer') + if (err) { + reject(err) + } else { + resolve() + } + }); + })) testProxyPort += 1; testProxyServer = stoppable(httpProxy(http.createServer())) @@ -109,7 +121,17 @@ describe('HTTP Request Node', function() { res.setHeader("x-testproxy-header", "foobar") } }) - testProxyServer.listen(testProxyPort) + console.log('> testProxyServer') + promises.push(new Promise((resolve, reject) => { + testProxyServer.listen(testProxyPort, function(err) { + console.log(' done testProxyServer') + if (err) { + reject(err) + } else { + resolve() + } + }) + })) testProxyAuthPort += 1 testProxyServerAuth = stoppable(httpProxy(http.createServer())) @@ -131,9 +153,19 @@ describe('HTTP Request Node', function() { res.setHeader("x-testproxy-header", "foobar") } }) - testProxyServerAuth.listen(testProxyAuthPort) + console.log('> testProxyServerAuth') + promises.push(new Promise((resolve, reject) => { + testProxyServerAuth.listen(testProxyAuthPort, function(err) { + console.log(' done testProxyServerAuth') + if (err) { + reject(err) + } else { + resolve() + } + }) + })) - done(err); + Promise.all(promises).then(() => { done() }).catch(done) }); } @@ -429,7 +461,11 @@ describe('HTTP Request Node', function() { if (err) { done(err); } - helper.startServer(done); + console.log('> helper.startServer') + helper.startServer(function(err) { + console.log('> helper started') + done(err) + }); }); });