From dee68b903c8e22d067edd7e6d55706830b4530e0 Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Sat, 13 May 2023 11:14:57 +0100 Subject: [PATCH 1/5] fix sub/unsub when using "dont unsub" --- .../@node-red/nodes/core/network/10-mqtt.js | 335 +++++++++++++----- 1 file changed, 250 insertions(+), 85 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 9e17463dd..1835582e6 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 @@ -219,8 +219,10 @@ module.exports = function(RED) { * Handle the payload / packet recieved in MQTT In and MQTT Sub nodes */ function subscriptionHandler(node, datatype ,topic, payload, packet) { - const v5 = node.brokerConn.options && node.brokerConn.options.protocolVersion == 5; - var msg = {topic:topic, payload:null, qos:packet.qos, retain:packet.retain}; + const msg = {topic:topic, payload:null, qos:packet.qos, retain:packet.retain}; + const v5 = (node && node.brokerConn) + ? node.brokerConn.v5() + : Object.prototype.hasOwnProperty.call(packet, "properties"); if(v5 && packet.properties) { setStrProp(packet.properties, msg, "responseTopic"); setBufferProp(packet.properties, msg, "correlationData"); @@ -300,7 +302,7 @@ module.exports = function(RED) { //} } msg.payload = payload; - if ((node.brokerConn.broker === "localhost")||(node.brokerConn.broker === "127.0.0.1")) { + if (node.brokerConn && (node.brokerConn.broker === "localhost" || node.brokerConn.broker === "127.0.0.1")) { msg._topic = topic; } node.send(msg); @@ -412,6 +414,12 @@ module.exports = function(RED) { } } + /** + * Perform the connect action + * @param {MQTTInNode|MQTTOutNode} node + * @param {Object} msg + * @param {Function} done + */ function handleConnectAction(node, msg, done) { let actionData = typeof msg.broker === 'object' ? msg.broker : null; if (node.brokerConn.canConnect()) { @@ -442,12 +450,17 @@ module.exports = function(RED) { } } + /** + * Perform the disconnect action + * @param {MQTTInNode|MQTTOutNode} node + * @param {Function} done + */ function handleDisconnectAction(node, done) { node.brokerConn.disconnect(function () { done(); }); } - + const unsubscribeCandidates = {} //#endregion "Supporting functions" //#region "Broker node" @@ -591,10 +604,9 @@ module.exports = function(RED) { if (typeof node.cleansession === 'undefined') { node.cleansession = true; } - if (typeof node.autoUnsubscribe === 'undefined') { + if (typeof node.autoUnsubscribe !== 'boolean') { node.autoUnsubscribe = true; } - //use url or build a url from usetls://broker:port if (node.url && node.brokerurl !== node.url) { node.brokerurl = node.url; @@ -664,7 +676,6 @@ module.exports = function(RED) { node.options.password = node.password; node.options.keepalive = node.keepalive; node.options.clean = node.cleansession; - node.options.autoUnsubscribe = node.autoUnsubscribe; node.options.clientId = node.clientid || 'nodered_' + RED.util.generateId(); node.options.reconnectPeriod = RED.settings.mqttReconnectTime||5000; delete node.options.protocolId; //V4+ default @@ -702,7 +713,8 @@ module.exports = function(RED) { node.options.rejectUnauthorized = (node.verifyservercert == "true" || node.verifyservercert === true); } } - + node.v5 = () => node.options && node.options.protocolVersion == 5 + node.subscriptionIdentifiersAvailable = () => node.v5() && node.serverProperties && node.serverProperties.subscriptionIdentifiersAvailable n.autoConnect = n.autoConnect === "false" || n.autoConnect === false ? false : true; node.setOptions(n, true); @@ -784,18 +796,11 @@ module.exports = function(RED) { // Re-subscribe to stored topics for (var s in node.subscriptions) { if (node.subscriptions.hasOwnProperty(s)) { - let topic = s; - let qos = 0; - let _options = {}; for (var r in node.subscriptions[s]) { if (node.subscriptions[s].hasOwnProperty(r)) { - qos = Math.max(qos,node.subscriptions[s][r].qos); - _options = node.subscriptions[s][r].options; - node._clientOn('message',node.subscriptions[s][r].handler); + node.subscribe(node.subscriptions[s][r]) } } - _options.qos = _options.qos || qos; - node.client.subscribe(topic, _options); } } @@ -857,22 +862,28 @@ module.exports = function(RED) { if(!node.client) { return _callback(); } if(node.closing) { return _callback(); } + /** + * Call end and wait for the client to end (or timeout) + * @param {mqtt.MqttClient} client The broker client + * @param {number} ms The time to wait for the client to end + * @returns + */ let waitEnd = (client, ms) => { return new Promise( (resolve, reject) => { node.closing = true; - if(!client) { + if (!client) { resolve(); - } else { + } else { const t = setTimeout(() => { //clean end() has exceeded WAIT_END, lets force end! client && client.end(true); - reject(); + resolve(); }, ms); client.end(() => { - clearTimeout(t); - resolve() - }); - } + clearTimeout(t); + resolve() + }); + } }); }; if(node.connected && node.closeMessage) { @@ -893,64 +904,222 @@ module.exports = function(RED) { } node.subscriptionIds = {}; node.subid = 1; - node.subscribe = function (topic,options,callback,ref) { - ref = ref||0; - var qos; - if(typeof options == "object") { - qos = options.qos; - } else { - qos = options; - options = {}; + + //typedef for subscription object: + /** + * @typedef {Object} Subscription + * @property {String} topic - topic to subscribe to + * @property {Object} [options] - options object + * @property {Number} [options.qos] - quality of service + * @property {Number} [options.nl] - no local + * @property {Number} [options.rap] - retain as published + * @property {Number} [options.rh] - retain handling + * @property {Number} [options.properties] - MQTT 5.0 properties + * @property {Number} [options.properties.subscriptionIdentifier] - MQTT 5.0 subscription identifier + * @property {Number} [options.properties.userProperties] - MQTT 5.0 user properties + * @property {Function} callback + * @property {String} ref - reference to the node that created the subscription + */ + + /** + * Create a subscription object + * @param {String} _topic - topic to subscribe to + * @param {Object} _options - options object + * @param {String} _ref - reference to the node that created the subscription + * @returns {Subscription} + */ + function createSubscriptionObject(_topic, _options, _ref, _brokerId) { + /** @type {Subscription} */ + const subscription = {}; + const ref = _ref || 0; + let options + let qos = 1 // default to QoS 1 (AWS and several other brokers don't support QoS 2) + + // if options is an object, then clone it + if (typeof _options == "object") { + options = RED.util.cloneMessage(_options || {}) + qos = _options.qos; + } else if (typeof _options == "number") { + qos = _options; } - options.qos = qos; + options = options || {}; + + // sanitise qos + if (typeof qos === "number" && qos >= 0 && qos <= 2) { + options.qos = qos; + } + + subscription.topic = _topic; + subscription.qos = qos; + subscription.options = RED.util.cloneMessage(options); + subscription.ref = ref; + subscription.brokerId = _brokerId; + return subscription; + } + + /** + * If topic is a subscription object, then use that, otherwise look up the topic in + * the subscriptions object. If the topic is not found, then create a new subscription + * object and add it to the subscriptions object. + * @param {Subscription|String} topic + * @param {*} options + * @param {*} callback + * @param {*} ref + */ + node.subscribe = function (topic, options, callback, ref) { + /** @type {Subscription} */ + let subscription + let doCompare = false + let changesFound = false + + // function signature 1: subscribe(subscription: Subscription) + if (typeof topic === "object" && topic !== null) { + subscription = topic + topic = subscription.topic + options = subscription.options + ref = subscription.ref + callback = subscription.callback + } + + // function signature 2: subscribe(topic: String, options: Object, callback: Function, ref: String) + else if (typeof topic === "string") { + // since this is a call where all params are provided, it might be + // a node change (modification) so we need to check for changes + doCompare = true + subscription = node.subscriptions[topic] && node.subscriptions[topic][ref] + } + + // bad function call + else { + console.warn('Invalid call to node.subscribe') + return + } + const thisBrokerId = node.type === 'mqtt-broker' ? node.id : node.broker + + // unsubscribe topics where the broker has changed + const oldBrokerSubs = (unsubscribeCandidates[ref] || []).filter(sub => sub.brokerId !== thisBrokerId) + oldBrokerSubs.forEach(sub => { + /** @type {MQTTBrokerNode} */ + const _brokerConn = RED.nodes.getNode(sub.brokerId) + if (_brokerConn) { + _brokerConn.unsubscribe(sub.topic, sub.ref, true) + } + }) + + // if subscription is found (or sent in as a parameter), then check for changes. + // if there are any changes requested, tidy up the old subscription + if (subscription) { + if (doCompare) { + // compare the current sub to the passed in parameters. Use RED.util.compareObjects against + // only the minimal set of properties to identify if the subscription has changed + const currentSubscription = createSubscriptionObject(subscription.topic, subscription.options, subscription.ref) + const newSubscription = createSubscriptionObject(topic, options, ref) + changesFound = RED.util.compareObjects(currentSubscription, newSubscription) === false + } + } + + if (changesFound) { + if (subscription.handler) { + node._clientRemoveListeners('message', subscription.handler) + subscription.handler = null + } + const _brokerConn = RED.nodes.getNode(subscription.brokerId) + if (_brokerConn) { + _brokerConn.unsubscribe(subscription.topic, subscription.ref, true) + } + } + + // clean up the unsubscribe candidate list + delete unsubscribeCandidates[ref] + + // determine if this is an existing subscription + const existingSubscription = typeof subscription === "object" && subscription !== null + + // if existing subscription is not found or has changed, create a new subscription object + if (existingSubscription === false || changesFound) { + subscription = createSubscriptionObject(topic, options, ref, node.id) + } + + // setup remainder of subscription properties and event handling + node.subscriptions[topic] = node.subscriptions[topic] || {}; + node.subscriptions[topic][ref] = subscription if (!node.subscriptionIds[topic]) { node.subscriptionIds[topic] = node.subid++; } - options.properties = options.properties || {}; - options.properties.subscriptionIdentifier = node.subscriptionIds[topic]; + subscription.options = subscription.options || {}; + subscription.options.properties = options.properties || {}; + subscription.options.properties.subscriptionIdentifier = node.subscriptionIds[topic]; + subscription.callback = callback; - node.subscriptions[topic] = node.subscriptions[topic]||{}; - var sub = { - topic:topic, - qos:qos, - options:options, - handler:function(mtopic,mpayload, mpacket) { - if(mpacket.properties && options.properties && mpacket.properties.subscriptionIdentifier && options.properties.subscriptionIdentifier && (mpacket.properties.subscriptionIdentifier !== options.properties.subscriptionIdentifier) ) { - //do nothing as subscriptionIdentifier does not match - } else if (matchTopic(topic,mtopic)) { - callback(mtopic,mpayload, mpacket); - } - }, - ref: ref - }; - node.subscriptions[topic][ref] = sub; + // if the client is connected, then setup the handler and subscribe if (node.connected) { - node._clientOn('message',sub.handler); - node.client.subscribe(topic, options); - } - }; + const subIdsAvailable = node.subscriptionIdentifiersAvailable() - node.unsubscribe = function (topic, ref, removed) { - ref = ref||0; - var sub = node.subscriptions[topic]; - if (sub) { - if (sub[ref]) { - if(node.client) { - node._clientRemoveListeners('message',sub[ref].handler); - } - delete sub[ref]; - } - //TODO: Review. The `if(removed)` was commented out to always delete and remove subscriptions. - // if we dont then property changes dont get applied and old subs still trigger - //if (removed) { - if (Object.keys(sub).length === 0) { - delete node.subscriptions[topic]; - delete node.subscriptionIds[topic]; - if (node.connected) { - node.client.unsubscribe(topic); + if (!subscription.handler) { + subscription.handler = function (mtopic, mpayload, mpacket) { + const sops = subscription.options ? subscription.options.properties : {} + const pops = mpacket.properties || {} + if (subIdsAvailable && pops.subscriptionIdentifier && sops.subscriptionIdentifier && (pops.subscriptionIdentifier !== sops.subscriptionIdentifier)) { + //do nothing as subscriptionIdentifier does not match + } else if (matchTopic(topic, mtopic)) { + subscription.callback && subscription.callback(mtopic, mpayload, mpacket) } } - //} + } + node._clientOn('message', subscription.handler) + // if the broker doesn't support subscription identifiers, then don't send them (AWS support) + if (subscription.options.properties && subscription.options.properties.subscriptionIdentifier && subIdsAvailable !== true) { + delete subscription.options.properties.subscriptionIdentifier + } + node.client.subscribe(topic, subscription.options) + } + + } + + node.unsubscribe = function (topic, ref, removeClientSubscription) { + ref = ref||0; + const unsub = removeClientSubscription || node.autoUnsubscribe !== false + const sub = node.subscriptions[topic]; + let brokerId = node.id + if (sub) { + if (sub[ref]) { + brokerId = sub[ref].brokerId || brokerId + if(node.client && sub[ref].handler) { + node._clientRemoveListeners('message', sub[ref].handler); + sub[ref].handler = null + } + if (unsub) { + delete sub[ref] + } + } + // if instructed to remove the actual MQTT client subscription + if (unsub) { + // if there are no more subscriptions for the topic, then remove the topic + if (Object.keys(sub).length === 0) { + try { + node.client.unsubscribe(topic) + } catch (_err) { + // do nothing + } finally { + // remove unsubscribe candidate as it is now REALLY unsubscribed + delete node.subscriptions[topic]; + delete node.subscriptionIds[topic]; + if (unsubscribeCandidates[ref]) { + unsubscribeCandidates[ref] = unsubscribeCandidates[ref].filter(sub => sub.topic !== topic) + } + } + } + } else { + // if instructed to not remove the client subscription, then add it to the candidate list + // of subscriptions to be removed when the the same ref is used in a subsequent subscribe + // and the topic has changed + unsubscribeCandidates[ref] = unsubscribeCandidates[ref] || []; + unsubscribeCandidates[ref].push({ + topic: topic, + ref: ref, + brokerId: brokerId + }) + } } }; node.topicAliases = {}; @@ -988,7 +1157,7 @@ module.exports = function(RED) { setStrProp(msg, options.properties, "contentType"); setIntProp(msg, options.properties, "messageExpiryInterval", 0); setUserProperties(msg.userProperties, options.properties); - setIntProp(msg, options.properties, "topicAlias", 1, node.serverProperties.topicAliasMaximum || 0); + setIntProp(msg, options.properties, "topicAlias", 1, bsp.topicAliasMaximum || 0); setBoolProp(msg, options.properties, "payloadFormatIndicator"); //FUTURE setIntProp(msg, options.properties, "subscriptionIdentifier", 1, 268435455); @@ -1124,7 +1293,7 @@ module.exports = function(RED) { if(node.rap === "true" || node.rap === true) options.rap = true; else if(node.rap === "false" || node.rap === false) options.rap = false; } - + node._topic = node.topic; // store the original topic incase node is later changed node.brokerConn.subscribe(node.topic,options,function(topic, payload, packet) { subscriptionHandler(node, node.datatype, topic, payload, packet); },node.id); @@ -1177,7 +1346,7 @@ module.exports = function(RED) { } if (action === Actions.UNSUBSCRIBE) { subscriptions.forEach(function (sub) { - node.brokerConn.unsubscribe(sub.topic, node.id); + node.brokerConn.unsubscribe(sub.topic, node.id, true); delete node.dynamicSubs[sub.topic]; }) //user can access current subscriptions through the complete node is so desired @@ -1187,7 +1356,7 @@ module.exports = function(RED) { subscriptions.forEach(function (sub) { //always unsubscribe before subscribe to prevent multiple subs to same topic if (node.dynamicSubs[sub.topic]) { - node.brokerConn.unsubscribe(sub.topic, node.id); + node.brokerConn.unsubscribe(sub.topic, node.id, true); delete node.dynamicSubs[sub.topic]; } @@ -1233,16 +1402,12 @@ module.exports = function(RED) { node.on('close', function(removed, done) { if (node.brokerConn) { if(node.isDynamic) { - if (node.brokerConn.options.autoUnsubscribe) { - Object.keys(node.dynamicSubs).forEach(function (topic) { - node.brokerConn.unsubscribe(topic, node.id, removed); - }); - node.dynamicSubs = {}; - } + Object.keys(node.dynamicSubs).forEach(function (topic) { + node.brokerConn.unsubscribe(topic, node.id, removed); + }); + node.dynamicSubs = {}; } else { - if (node.brokerConn.options.autoUnsubscribe) { - node.brokerConn.unsubscribe(node.topic, node.id, removed); - } + node.brokerConn.unsubscribe(node.topic, node.id, removed); } node.brokerConn.deregister(node, done, removed); node.brokerConn = null; From cfa25dc655dad748506107c16ccc9a9ca942e4e9 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 25 May 2023 17:30:17 +0100 Subject: [PATCH 2/5] Update xml2js --- package.json | 2 +- .../@node-red/nodes/core/parsers/70-XML.js | 23 +------------------ .../node_modules/@node-red/nodes/package.json | 2 +- 3 files changed, 3 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index f53cad7c6..962204226 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "uglify-js": "3.17.4", "uuid": "9.0.0", "ws": "7.5.6", - "xml2js": "0.5.0" + "xml2js": "0.6.0" }, "optionalDependencies": { "bcrypt": "5.1.0" diff --git a/packages/node_modules/@node-red/nodes/core/parsers/70-XML.js b/packages/node_modules/@node-red/nodes/core/parsers/70-XML.js index 538368730..66fa42cdc 100644 --- a/packages/node_modules/@node-red/nodes/core/parsers/70-XML.js +++ b/packages/node_modules/@node-red/nodes/core/parsers/70-XML.js @@ -33,14 +33,7 @@ module.exports = function(RED) { parseString(value, options, function (err, result) { if (err) { done(err); } else { - // TODO: With xml2js@0.5.0, they return an object with - // a null prototype. This could cause unexpected - // issues. So for now, we have to reconstruct - // the object with a proper prototype. - // Once https://github.com/Leonidas-from-XIV/node-xml2js/pull/674 - // is merged, we can revisit and hopefully remove this hack - value = fixObj(result) - RED.util.setMessageProperty(msg,node.property,value); + RED.util.setMessageProperty(msg,node.property,result); send(msg); done(); } @@ -52,18 +45,4 @@ module.exports = function(RED) { }); } RED.nodes.registerType("xml",XMLNode); - - - function fixObj(obj) { - const res = {} - const keys = Object.keys(obj) - keys.forEach(k => { - if (typeof obj[k] === 'object' && obj[k]) { - res[k] = fixObj(obj[k]) - } else { - res[k] = obj[k] - } - }) - return res - } } diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json index 6bed85bd9..b12c0a2ff 100644 --- a/packages/node_modules/@node-red/nodes/package.json +++ b/packages/node_modules/@node-red/nodes/package.json @@ -44,7 +44,7 @@ "tough-cookie": "4.1.2", "uuid": "9.0.0", "ws": "7.5.6", - "xml2js": "0.5.0", + "xml2js": "0.6.0", "iconv-lite": "0.6.3" } } From 614834090ec7bf9af54d2926f7560691b63a00d8 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 25 May 2023 18:10:01 +0100 Subject: [PATCH 3/5] Bump everything for beta.3 --- CHANGELOG.md | 35 +++++++++++++++++++ package.json | 2 +- .../@node-red/editor-api/package.json | 6 ++-- .../@node-red/editor-client/package.json | 2 +- .../editor-client/src/tours/welcome.js | 6 ++-- .../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, 55 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a82583bd..80bf47576 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,38 @@ +#### 3.1.0-beta.3: Beta Release + +Editor + + - Select the item that is specified in a deep link URL (#4113) @Steve-Mcl + - Update to Monaco 0.38.0 (#4189) @Steve-Mcl + - Place subflow outputs/inputs relative to current view (#4183) @knolleary + - Enable RED.view.select to select group by id (#4184) @knolleary + - Combine existing env vars when merging groups (#4182) @knolleary + - Avoid creating empty global-config node if not needed (#4153) @knolleary + - Fix group selection when using lasso (#4108) @knolleary + - Use editor path in generating localStorage keys (#4151) @mw75 + - Ensure no node credentials are included when exporting to clipboard (#4112) @knolleary + - Fix jsonata expression test ui (#4097) @knolleary + - Fix search button in palette popover (#4096) @knolleary + +Runtime + + - Allow options object on each httpStatic configuration (#4109) @kevinGodell + - Ensure non-zero exit codes for errors (#4181) @knolleary + - Ensure external modules are installed synchronously (#4180) @knolleary + - Update dependecies include got (#4155) @knolleary + - Add Japanese translations for v3.1 beta.2 (#4158) @kazuhitoyokoi + - Ensure express server options are applied consistently (#4178) @knolleary + - Remove version info from theme endpoint (#4179) @knolleary + - Add Japanese translations for welcome tour of 3.1.0 beta.2 (#4145) @kazuhitoyokoi + - Added SHA-256 and SHA-512-256 digest authentication (#4100) @sroebert + - Add "timers" types to known types (#4103) @Steve-Mcl + +Nodes + + - Allow Catch/Status nodes to be scoped to their group (#4185) @NetHans + - MQTT: Option to disable MQTT topic unsubscribe on disconnect (#4078) @flying7eleven + + #### 3.1.0-beta.2: Beta Release Editor diff --git a/package.json b/package.json index f53cad7c6..babf008e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "3.1.0-beta.2", + "version": "3.1.0-beta.3", "description": "Low-code programming for event-driven applications", "homepage": "http://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 6d3ddb5af..a19f95ad6 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": "3.1.0-beta.2", + "version": "3.1.0-beta.3", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/util": "3.1.0-beta.2", - "@node-red/editor-client": "3.1.0-beta.2", + "@node-red/util": "3.1.0-beta.3", + "@node-red/editor-client": "3.1.0-beta.3", "bcryptjs": "2.4.3", "body-parser": "1.20.2", "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 c81d2dfc3..b38970bb9 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": "3.1.0-beta.2", + "version": "3.1.0-beta.3", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/editor-client/src/tours/welcome.js b/packages/node_modules/@node-red/editor-client/src/tours/welcome.js index e3c50d229..eb89c3b5e 100644 --- a/packages/node_modules/@node-red/editor-client/src/tours/welcome.js +++ b/packages/node_modules/@node-red/editor-client/src/tours/welcome.js @@ -1,14 +1,14 @@ export default { - version: "3.1.0-beta.2", + version: "3.1.0-beta.3", steps: [ { titleIcon: "fa fa-map-o", title: { - "en-US": "Welcome to Node-RED 3.1 Beta 2!", + "en-US": "Welcome to Node-RED 3.1 Beta 3!", "ja": "Node-RED 3.1 ベータ2へようこそ!" }, description: { - "en-US": "

This is the second beta release for 3.1.0 and we have a few new features to tell you about.

", + "en-US": "

This is the third beta release for 3.1.0. This is mostly a bug fix release, so you can skip this tour if you've tried the other betas.

If not, stick around to see what's new in Node-RED 3.1.

", "ja": "

これは3.1.0の2回目のベータリリースです。いくつかの新機能について説明します。

" } }, diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json index 6bed85bd9..4faf5a379 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": "3.1.0-beta.2", + "version": "3.1.0-beta.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 fae0d624a..3a5363a50 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": "3.1.0-beta.2", + "version": "3.1.0-beta.3", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,7 +16,7 @@ } ], "dependencies": { - "@node-red/util": "3.1.0-beta.2", + "@node-red/util": "3.1.0-beta.3", "clone": "2.1.2", "fs-extra": "11.1.1", "semver": "7.5.0", diff --git a/packages/node_modules/@node-red/runtime/package.json b/packages/node_modules/@node-red/runtime/package.json index 7504a7497..ea82bb614 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": "3.1.0-beta.2", + "version": "3.1.0-beta.3", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/registry": "3.1.0-beta.2", - "@node-red/util": "3.1.0-beta.2", + "@node-red/registry": "3.1.0-beta.3", + "@node-red/util": "3.1.0-beta.3", "async-mutex": "0.4.0", "clone": "2.1.2", "express": "4.18.2", diff --git a/packages/node_modules/@node-red/util/package.json b/packages/node_modules/@node-red/util/package.json index 184e96f38..419bbc9f8 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": "3.1.0-beta.2", + "version": "3.1.0-beta.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 0b899f655..c9db68e77 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": "3.1.0-beta.2", + "version": "3.1.0-beta.3", "description": "Low-code programming for event-driven applications", "homepage": "http://nodered.org", "license": "Apache-2.0", @@ -31,10 +31,10 @@ "flow" ], "dependencies": { - "@node-red/editor-api": "3.1.0-beta.2", - "@node-red/runtime": "3.1.0-beta.2", - "@node-red/util": "3.1.0-beta.2", - "@node-red/nodes": "3.1.0-beta.2", + "@node-red/editor-api": "3.1.0-beta.3", + "@node-red/runtime": "3.1.0-beta.3", + "@node-red/util": "3.1.0-beta.3", + "@node-red/nodes": "3.1.0-beta.3", "basic-auth": "2.0.1", "bcryptjs": "2.4.3", "express": "4.18.2", From 9423104daddf989824e41fcb09962241f22bf591 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Fri, 26 May 2023 12:35:53 +0900 Subject: [PATCH 4/5] Add Japanese translations for v3.1.0-beta.3 --- .../node_modules/@node-red/editor-client/src/tours/welcome.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/tours/welcome.js b/packages/node_modules/@node-red/editor-client/src/tours/welcome.js index eb89c3b5e..d6305e01b 100644 --- a/packages/node_modules/@node-red/editor-client/src/tours/welcome.js +++ b/packages/node_modules/@node-red/editor-client/src/tours/welcome.js @@ -5,11 +5,11 @@ export default { titleIcon: "fa fa-map-o", title: { "en-US": "Welcome to Node-RED 3.1 Beta 3!", - "ja": "Node-RED 3.1 ベータ2へようこそ!" + "ja": "Node-RED 3.1 ベータ3へようこそ!" }, description: { "en-US": "

This is the third beta release for 3.1.0. This is mostly a bug fix release, so you can skip this tour if you've tried the other betas.

If not, stick around to see what's new in Node-RED 3.1.

", - "ja": "

これは3.1.0の2回目のベータリリースです。いくつかの新機能について説明します。

" + "ja": "

これは3.1.0の3回目のベータリリースです。不具合修正のリリースのため、もし他のベータ版を試したことがある場合は、このツアーを読み飛ばしてもかまいません。

そうでない場合は、Node-RED 3.1の新機能を確認してください。

" } }, { From 59ec87a3939cedf77b263b4ca3910a1e6e8b1a39 Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Fri, 26 May 2023 10:12:48 +0100 Subject: [PATCH 5/5] fix function node error badge not shown fixes #4194 --- .../src/js/ui/editors/code-editors/monaco.js | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) 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 e0bd94c8b..b18e01fbb 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 @@ -1168,19 +1168,19 @@ RED.editor.codeEditor.monaco = (function() { // Warning: 4 // Error: 8 ed.getAnnotations = function getAnnotations() { - var aceCompatibleMarkers = []; + let aceCompatibleMarkers; try { - var _model = ed.getModel(); + const _model = ed.getModel(); if (_model !== null) { - var id = _model._languageId; // e.g. javascript - var ra = _model._associatedResource.authority; //e.g. model - var rp = _model._associatedResource.path; //e.g. /18 - var rs = _model._associatedResource.scheme; //e.g. inmemory - var modelMarkers = monaco.editor.getModelMarkers(_model) || []; - var thisEditorsMarkers = modelMarkers.filter(function (marker) { - var _ra = marker.resource.authority; //e.g. model - var _rp = marker.resource.path; //e.g. /18 - var _rs = marker.resource.scheme; //e.g. inmemory + const id = _model.getLanguageId(); // e.g. javascript + const ra = _model.uri.authority; // e.g. model + const rp = _model.uri.path; // e.g. /18 + const rs = _model.uri.scheme; // e.g. inmemory + const modelMarkers = monaco.editor.getModelMarkers(_model) || []; + const thisEditorsMarkers = modelMarkers.filter(function (marker) { + const _ra = marker.resource.authority; // e.g. model + const _rp = marker.resource.path; // e.g. /18 + const _rs = marker.resource.scheme; // e.g. inmemory return marker.owner == id && _ra === ra && _rp === rp && _rs === rs; }) aceCompatibleMarkers = thisEditorsMarkers.map(function (marker) {