mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Compare commits
77 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
f91762ec8f | ||
|
7f698610df | ||
|
bee06f64f5 | ||
|
53af4231af | ||
|
2ea4b15dca | ||
|
ce09f56dcc | ||
|
6c648e4bab | ||
|
72661aa159 | ||
|
16e79d6bd2 | ||
|
726fac97d2 | ||
|
efc3882961 | ||
|
489c456271 | ||
|
970be7b5f2 | ||
|
b94fdd2f79 | ||
|
66f6bc59fa | ||
|
455f2dc800 | ||
|
aed2b8e46d | ||
|
633afbbb44 | ||
|
6c797e35cf | ||
|
30b8a95421 | ||
|
7665971b52 | ||
|
ba6dc3117f | ||
|
73fcc05a54 | ||
|
fd409b0636 | ||
|
29287149b4 | ||
|
18c0ec990a | ||
|
ee2b151b7a | ||
|
2d7a068644 | ||
|
5c5855751c | ||
|
396b3b64ff | ||
|
48e33a472b | ||
|
a7375e4e8f | ||
|
42732db2b0 | ||
|
7c830856df | ||
|
5dad33dc77 | ||
|
cbdb988d02 | ||
|
e944dfecc2 | ||
|
b2c2b9bd14 | ||
|
0ed7f6ce4e | ||
|
74e3b9e9ed | ||
|
7fd884c68f | ||
|
3f29283724 | ||
|
ea303495b0 | ||
|
9d39709038 | ||
|
facb6143c3 | ||
|
68a2786ff2 | ||
|
b2b31670f8 | ||
|
355fb724f8 | ||
|
d925f12aea | ||
|
428ed090f3 | ||
|
9946132658 | ||
|
30f08a9f17 | ||
|
83f1729d83 | ||
|
3667de18a9 | ||
|
bda8e86ea4 | ||
|
7920fa1dd4 | ||
|
cb06690553 | ||
|
4b4e5a0861 | ||
|
e5952f082e | ||
|
c7994b6393 | ||
|
39ac6b0d46 | ||
|
e22bf22b61 | ||
|
92dd80ac49 | ||
|
9442e2a993 | ||
|
6df7f50f9f | ||
|
933cad888f | ||
|
8133c5e834 | ||
|
e7f6549cb6 | ||
|
2ded07c765 | ||
|
6a799e7e2a | ||
|
1d40378f8c | ||
|
bcab1cf096 | ||
|
03484fd5cd | ||
|
1eea371124 | ||
|
02d9ad479d | ||
|
3be75fa822 | ||
|
ca204dea2d |
60
CHANGELOG.md
60
CHANGELOG.md
@@ -1,3 +1,63 @@
|
||||
#### 2.2.3: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Fix storing subflow credential type when input has multiple types (backport to v2.x) (#3828) @Steve-Mcl
|
||||
- Fix markdown editor to mark up orderedlists correctly
|
||||
- Ensure 'hidden flow' count doesn't include subflows (backport to v2.x) (#3823) @Steve-Mcl
|
||||
- Fix credential type input item of subflow template (backport to v2.x) (#3822) @Steve-Mcl
|
||||
- Fix to extend escaped subflow category characters (backport #3647 to v2.x) (#3665) @Steve-Mcl
|
||||
- Reset mouse state when switching tabs (Backport #3643 to v2.x) (#3664) @Steve-Mcl
|
||||
- Fix to sanitize tab name (Backport #3646 to v2.x) (#3663) @Steve-Mcl
|
||||
- fix new folder menu of save to library dialog (backport #3633 to v2.x) (#3661) @Steve-Mcl
|
||||
- Ensure ACE worker options are set (Backport 3611 to v2.x) (#3657) @Steve-Mcl
|
||||
- Fix browser console error Uncaught TypeError when searching certain terms (Backport #3548 to v2.x) (#3656) @Steve-Mcl
|
||||
- Remove duplicate history add of ungroup event (backport #3581to v2.x) (#3655) @Steve-Mcl
|
||||
- Don't let themes change node config colors (#3564) @bonanitech
|
||||
- Fix gap between typedInput containers borders (#3560) @bonanitech
|
||||
- Fix recording removed links in edit history (#3547) @knolleary
|
||||
- Remove unused SASS vars (#3536) @bonanitech
|
||||
- Add custom style for jQuery widgets borders (#3537) @bonanitech
|
||||
- fix out of scope reference of hasUnusedConfig variable (#3535) @HiroyasuNishiyama
|
||||
- correct "non string" check parenthesis (#3524) @Steve-Mcl
|
||||
- Ensure i18n of scoped package name (#3516) @Steve-Mcl
|
||||
- Prevent shortcut deploy when deploy button shaded (#3517) @Steve-Mcl
|
||||
- Fix: Sidebar "Configuration" filter button tooltip (#3500) @ralphwetzel
|
||||
- Add the ability to customize diff colors even more (#3499) @bonanitech
|
||||
- do JSON comparison of old value/new value (#3481) @Steve-Mcl
|
||||
- fix nodes losing their wires when in an iframe (#3484) @zettca
|
||||
- Improve scroll into view (#3468) @Steve-Mcl
|
||||
- Dont show 1st tab if hidden when loading (#3464) @Steve-Mcl
|
||||
|
||||
Runtime
|
||||
|
||||
- Do not remove unknown credentials of Subflow Modules (backport to v2.x) (#3826) @Steve-Mcl
|
||||
- Backports to v2.x (#3735) @kazuhitoyokoi
|
||||
- Backports to v2.x (#3705) @kazuhitoyokoi
|
||||
- fix buffer parse error message of evaluateNodeProperty (backports #3624 for v2.x) (#3659) @Steve-Mcl
|
||||
- Don't start if user has no home directory (#3540) @hardillb
|
||||
- Error on invalid encrypted credentials (#3498) @sammachin
|
||||
|
||||
Nodes
|
||||
|
||||
- Allow HTTP Headers not in spec (backport to v2.x) (#3829) @Steve-Mcl
|
||||
- Fix change node, not handling from field properly when using context (backport to v2.x) (#3827) @Steve-Mcl
|
||||
- undo regression to tcp-in node
|
||||
- Fix CSV node to handle when outputting text fields (backport to v2.x) (#3825) @Steve-Mcl
|
||||
- Fix delay rate limit last timing when empty (backport to v2.x) (#3824) @Steve-Mcl
|
||||
- Join-reduce keep existing msg properties (backport to v2.x) (#3821) @Steve-Mcl
|
||||
- Fix ESM module loading in Function node (Backport #3645 to v2.x) (#3662) @Steve-Mcl
|
||||
- Fix JSONata evaluation of inject button (Backport 3632 to v2.x) (#3660) @Steve-Mcl
|
||||
- Dont delete TCP socket twice (backport Backport: #3630 to v2.x) (#3658) @Steve-Mcl
|
||||
- Mqtt fixes in v3 for v2.x (backports #3563 #3594 #3626 to v2.x) (#3654) @Steve-Mcl
|
||||
- MQTT Node - save and restore v5 config user props (#3562) @Steve-Mcl
|
||||
- Fix incorrect MQTT status (#3552) @Steve-Mcl
|
||||
- fix reference error of msg.status in debug node (#3526) @HiroyasuNishiyama
|
||||
- call done after ws disconnects (#3531) @Steve-Mcl
|
||||
- Add unit tests for MQTT nodes (#3497) @Steve-Mcl
|
||||
- fix typo of will properties (properies) (#3502) @Steve-Mcl
|
||||
- fix: ensure mqtt v5 props can be set false (#3472) @Steve-Mcl
|
||||
|
||||
#### 2.2.2: Maintenance Release
|
||||
|
||||
Nodes
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red",
|
||||
"version": "2.2.2",
|
||||
"version": "2.2.3",
|
||||
"description": "Low-code programming for event-driven applications",
|
||||
"homepage": "http://nodered.org",
|
||||
"license": "Apache-2.0",
|
||||
@@ -84,8 +84,8 @@
|
||||
"bcrypt": "5.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dompurify": "2.3.5",
|
||||
"grunt": "1.4.1",
|
||||
"dompurify": "2.3.6",
|
||||
"grunt": "1.5.2",
|
||||
"grunt-chmod": "~1.1.1",
|
||||
"grunt-cli": "~1.4.3",
|
||||
"grunt-concurrent": "3.0.0",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/editor-api",
|
||||
"version": "2.2.2",
|
||||
"version": "2.2.3",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -16,8 +16,8 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/util": "2.2.2",
|
||||
"@node-red/editor-client": "2.2.2",
|
||||
"@node-red/util": "2.2.3",
|
||||
"@node-red/editor-client": "2.2.3",
|
||||
"bcryptjs": "2.4.3",
|
||||
"body-parser": "1.19.1",
|
||||
"clone": "2.1.2",
|
||||
|
@@ -845,7 +845,7 @@
|
||||
"pushFailed": "リモートに新しいコミットがあるため、プッシュに失敗しました。プルしてマージしてから、再度プッシュしてください。",
|
||||
"push": "プッシュ",
|
||||
"pull": "プル",
|
||||
"unablePull": "<p>リモートの変更のプル失敗:ステージングされていないローカルの変更を上書きされてしまいます。</p><p>変更をコミットしてから再度実行してください。</p>",
|
||||
"unablePull": "<p>リモートの変更のプル失敗:ステージングされていないローカルの変更が上書きされてしまいます。</p><p>変更をコミットしてから再度実行してください。</p>",
|
||||
"showUnstagedChanges": "ステージングされていない変更を表示",
|
||||
"connectionFailed": "リモートリポジトリに接続できません: ",
|
||||
"pullUnrelatedHistory": "<p>リモートに関連のないコミット履歴があります。</p><p>本当に変更をプルしてローカルリポジトリに反映しますか?</p>",
|
||||
@@ -1287,6 +1287,10 @@
|
||||
"zoom-in": "ズームイン",
|
||||
"zoom-out": "ズームアウト",
|
||||
"zoom-reset": "ズームリセット",
|
||||
"toggle-navigator": "ナビゲータ表示切替"
|
||||
"toggle-navigator": "ナビゲータ表示切替",
|
||||
"new-project": "新しいプロジェクト",
|
||||
"open-project": "プロジェクトを開く",
|
||||
"show-project-settings": "プロジェクト設定を表示",
|
||||
"show-version-control-tab": "バージョンコントロールタブを表示"
|
||||
}
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@
|
||||
"tip14": "[shift] を押しながらノードを [click] すると、接続された全てのノードを選択できます。",
|
||||
"tip15": "[ctrl] を押しながらノードを [click] すると、選択/非選択を切り替えできます。",
|
||||
"tip16": "{{core:show-previous-tab}} や {{core:show-next-tab}} で、タブの切り替えができます。",
|
||||
"tip17": "ノードのプロバティ設定画面にて {{core:confirm-edit-tray}} を押すと、変更を確定できます。また、 {{core:cancel-edit-tray}} を押すと、変更を取り消せます。",
|
||||
"tip17": "ノードのプロパティ設定画面にて {{core:confirm-edit-tray}} を押すと、変更を確定できます。また、 {{core:cancel-edit-tray}} を押すと、変更を取り消せます。",
|
||||
"tip18": "ノードを選択し、 {{core:edit-selected-node}} を押すとプロパティ設定画面が表示されます。"
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/editor-client",
|
||||
"version": "2.2.2",
|
||||
"version": "2.2.3",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@@ -98,7 +98,7 @@ oop.inherits(NRJavaScriptWorker, Mirror);
|
||||
if (options) {
|
||||
for (var opt in options) {
|
||||
if (options.hasOwnProperty(opt)) {
|
||||
this.options[opt] = options.opt;
|
||||
this.options[opt] = options[opt];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -828,7 +828,7 @@ RED.tabs = (function() {
|
||||
}
|
||||
|
||||
// link.attr("title",tab.label);
|
||||
RED.popover.tooltip(link,function() { return tab.label})
|
||||
RED.popover.tooltip(link,function() { return RED.utils.sanitize(tab.label); });
|
||||
|
||||
if (options.onadd) {
|
||||
options.onadd(tab);
|
||||
|
@@ -41,8 +41,12 @@ RED.editor.envVarList = (function() {
|
||||
style: "width:100%",
|
||||
class: "node-input-env-value",
|
||||
type: "text",
|
||||
}).attr("autocomplete","disable").appendTo(envRow)
|
||||
valueField.typedInput({default:'str',types:isTemplateNode?DEFAULT_ENV_TYPE_LIST:DEFAULT_ENV_TYPE_LIST_INC_CRED});
|
||||
}).attr("autocomplete","disable").appendTo(envRow);
|
||||
var types = (opt.ui && opt.ui.opts && opt.ui.opts.types);
|
||||
if (!types) {
|
||||
types = isTemplateNode ? DEFAULT_ENV_TYPE_LIST : DEFAULT_ENV_TYPE_LIST_INC_CRED;
|
||||
}
|
||||
valueField.typedInput({default:'str',types:types});
|
||||
valueField.typedInput('type', opt.type);
|
||||
if (opt.type === "cred") {
|
||||
if (opt.value) {
|
||||
@@ -94,6 +98,11 @@ RED.editor.envVarList = (function() {
|
||||
}
|
||||
opt.ui.label = opt.ui.label || {};
|
||||
opt.ui.type = opt.ui.type || "input";
|
||||
if ((opt.ui.type === "cred") &&
|
||||
opt.ui.opts &&
|
||||
opt.ui.opts.types) {
|
||||
opt.ui.type = "input";
|
||||
}
|
||||
|
||||
var uiRow = $('<div/>').appendTo(container).hide();
|
||||
// save current info for reverting on cancel
|
||||
|
@@ -37,8 +37,7 @@
|
||||
if (!node._def.defaults || !node._def.defaults.hasOwnProperty("icon")) {
|
||||
var icon = $("#red-ui-editor-node-icon").val()||"";
|
||||
if (!this.isDefaultIcon) {
|
||||
if ((icon !== node.icon) &&
|
||||
(icon !== "")) {
|
||||
if ((node.icon && icon !== node.icon) || (!node.icon && icon !== "")) {
|
||||
editState.changes.icon = node.icon;
|
||||
node.icon = icon;
|
||||
editState.changed = true;
|
||||
|
@@ -323,9 +323,6 @@ RED.group = (function() {
|
||||
groups: [ ],
|
||||
dirty: RED.nodes.dirty()
|
||||
}
|
||||
RED.history.push(historyEvent);
|
||||
|
||||
|
||||
groups.forEach(function(g) {
|
||||
newSelection = newSelection.concat(ungroup(g))
|
||||
historyEvent.groups.push(g);
|
||||
|
@@ -363,106 +363,112 @@ RED.library = (function() {
|
||||
options.onconfirm(item);
|
||||
}
|
||||
});
|
||||
var itemTools = $("<div>").css({position: "absolute",bottom:"6px",right:"8px"});
|
||||
var menuButton = $('<button class="red-ui-button red-ui-button-small" type="button"><i class="fa fa-ellipsis-h"></i></button>')
|
||||
.on("click", function(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
var elementPos = menuButton.offset();
|
||||
|
||||
var menuOptionMenu = RED.menu.init({id:"red-ui-library-browser-menu",
|
||||
options: [
|
||||
{id:"red-ui-library-browser-menu-addFolder",label:RED._("library.newFolder"), onselect: function() {
|
||||
var defaultFolderName = "new-folder";
|
||||
var defaultFolderNameMatches = {};
|
||||
|
||||
var selected = dirList.treeList('selected');
|
||||
if (!selected.children) {
|
||||
selected = selected.parent;
|
||||
}
|
||||
var complete = function() {
|
||||
selected.children.forEach(function(c) {
|
||||
if (/^new-folder/.test(c.label)) {
|
||||
defaultFolderNameMatches[c.label] = true
|
||||
}
|
||||
});
|
||||
var folderIndex = 2;
|
||||
while(defaultFolderNameMatches[defaultFolderName]) {
|
||||
defaultFolderName = "new-folder-"+(folderIndex++)
|
||||
}
|
||||
|
||||
selected.treeList.expand();
|
||||
var input = $('<input type="text" class="red-ui-treeList-input">').val(defaultFolderName);
|
||||
var newItem = {
|
||||
icon: "fa fa-folder-o",
|
||||
children:[],
|
||||
path: selected.path,
|
||||
element: input
|
||||
}
|
||||
var confirmAdd = function() {
|
||||
var val = input.val().trim();
|
||||
if (val === "") {
|
||||
cancelAdd();
|
||||
return;
|
||||
} else {
|
||||
for (var i=0;i<selected.children.length;i++) {
|
||||
if (selected.children[i].label === val) {
|
||||
cancelAdd();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
newItem.treeList.remove();
|
||||
var finalItem = {
|
||||
library: selected.library,
|
||||
type: selected.type,
|
||||
icon: "fa fa-folder",
|
||||
children:[],
|
||||
label: val,
|
||||
path: newItem.path+val+"/"
|
||||
}
|
||||
selected.treeList.addChild(finalItem,true);
|
||||
}
|
||||
var cancelAdd = function() {
|
||||
newItem.treeList.remove();
|
||||
}
|
||||
input.on('keydown', function(evt) {
|
||||
evt.stopPropagation();
|
||||
if (evt.keyCode === 13) {
|
||||
confirmAdd();
|
||||
} else if (evt.keyCode === 27) {
|
||||
cancelAdd();
|
||||
}
|
||||
})
|
||||
input.on("blur", function() {
|
||||
confirmAdd();
|
||||
})
|
||||
selected.treeList.addChild(newItem);
|
||||
setTimeout(function() {
|
||||
input.trigger("focus");
|
||||
input.select();
|
||||
},400);
|
||||
}
|
||||
selected.treeList.expand(complete);
|
||||
|
||||
} },
|
||||
// null,
|
||||
// {id:"red-ui-library-browser-menu-rename",label:"Rename", onselect: function() {} },
|
||||
// {id:"red-ui-library-browser-menu-delete",label:"Delete", onselect: function() {} }
|
||||
]
|
||||
}).on('mouseleave', function(){ $(this).remove(); dirList.focus() })
|
||||
.on('mouseup', function() { var self = $(this);self.hide(); dirList.focus(); setTimeout(function() { self.remove() },100)})
|
||||
.appendTo("body");
|
||||
menuOptionMenu.css({
|
||||
position: "absolute",
|
||||
top: elementPos.top+"px",
|
||||
left: (elementPos.left - menuOptionMenu.width() + 20)+"px"
|
||||
}).show();
|
||||
|
||||
}).appendTo(itemTools);
|
||||
var itemTools = null;
|
||||
if (options.folderTools) {
|
||||
dirList.on('treelistselect', function(event, item) {
|
||||
if (item.writable !== false && item.treeList) {
|
||||
if (itemTools) {
|
||||
itemTools.remove();
|
||||
}
|
||||
itemTools = $("<div>").css({position: "absolute",bottom:"6px",right:"8px"});
|
||||
var menuButton = $('<button class="red-ui-button red-ui-button-small" type="button"><i class="fa fa-ellipsis-h"></i></button>')
|
||||
.on("click", function(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
var elementPos = menuButton.offset();
|
||||
|
||||
var menuOptionMenu
|
||||
= RED.menu.init({id:"red-ui-library-browser-menu",
|
||||
options: [
|
||||
{id:"red-ui-library-browser-menu-addFolder",label:RED._("library.newFolder"), onselect: function() {
|
||||
var defaultFolderName = "new-folder";
|
||||
var defaultFolderNameMatches = {};
|
||||
|
||||
var selected = dirList.treeList('selected');
|
||||
if (!selected.children) {
|
||||
selected = selected.parent;
|
||||
}
|
||||
var complete = function() {
|
||||
selected.children.forEach(function(c) {
|
||||
if (/^new-folder/.test(c.label)) {
|
||||
defaultFolderNameMatches[c.label] = true
|
||||
}
|
||||
});
|
||||
var folderIndex = 2;
|
||||
while(defaultFolderNameMatches[defaultFolderName]) {
|
||||
defaultFolderName = "new-folder-"+(folderIndex++)
|
||||
}
|
||||
|
||||
selected.treeList.expand();
|
||||
var input = $('<input type="text" class="red-ui-treeList-input">').val(defaultFolderName);
|
||||
var newItem = {
|
||||
icon: "fa fa-folder-o",
|
||||
children:[],
|
||||
path: selected.path,
|
||||
element: input
|
||||
}
|
||||
var confirmAdd = function() {
|
||||
var val = input.val().trim();
|
||||
if (val === "") {
|
||||
cancelAdd();
|
||||
return;
|
||||
} else {
|
||||
for (var i=0;i<selected.children.length;i++) {
|
||||
if (selected.children[i].label === val) {
|
||||
cancelAdd();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
newItem.treeList.remove();
|
||||
var finalItem = {
|
||||
library: selected.library,
|
||||
type: selected.type,
|
||||
icon: "fa fa-folder",
|
||||
children:[],
|
||||
label: val,
|
||||
path: newItem.path+val+"/"
|
||||
}
|
||||
selected.treeList.addChild(finalItem,true);
|
||||
}
|
||||
var cancelAdd = function() {
|
||||
newItem.treeList.remove();
|
||||
}
|
||||
input.on('keydown', function(evt) {
|
||||
evt.stopPropagation();
|
||||
if (evt.keyCode === 13) {
|
||||
confirmAdd();
|
||||
} else if (evt.keyCode === 27) {
|
||||
cancelAdd();
|
||||
}
|
||||
})
|
||||
input.on("blur", function() {
|
||||
confirmAdd();
|
||||
})
|
||||
selected.treeList.addChild(newItem);
|
||||
setTimeout(function() {
|
||||
input.trigger("focus");
|
||||
input.select();
|
||||
},400);
|
||||
}
|
||||
selected.treeList.expand(complete);
|
||||
|
||||
} },
|
||||
// null,
|
||||
// {id:"red-ui-library-browser-menu-rename",label:"Rename", onselect: function() {} },
|
||||
// {id:"red-ui-library-browser-menu-delete",label:"Delete", onselect: function() {} }
|
||||
]
|
||||
}).on('mouseleave', function(){ $(this).remove(); dirList.focus() })
|
||||
.on('mouseup', function() { var self = $(this);self.hide(); dirList.focus(); setTimeout(function() { self.remove() },100)})
|
||||
.appendTo("body");
|
||||
menuOptionMenu.css({
|
||||
position: "absolute",
|
||||
top: elementPos.top+"px",
|
||||
left: (elementPos.left - menuOptionMenu.width() + 20)+"px"
|
||||
}).show();
|
||||
|
||||
}).appendTo(itemTools);
|
||||
|
||||
itemTools.appendTo(item.treeList.label);
|
||||
}
|
||||
});
|
||||
|
@@ -208,7 +208,7 @@ RED.palette = (function() {
|
||||
}
|
||||
|
||||
function escapeCategory(category) {
|
||||
return category.replace(/[ /.]/g,"_");
|
||||
return category.replace(/[\x00-\x2c\x2e-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]/g,"_");
|
||||
}
|
||||
function addNodeType(nt,def) {
|
||||
if (getPaletteNode(nt).length) {
|
||||
|
@@ -141,7 +141,7 @@ RED.search = (function() {
|
||||
var key = keys[i];
|
||||
var kpos = keys[i].indexOf(val);
|
||||
if (kpos > -1) {
|
||||
var ids = Object.keys(index[key]);
|
||||
var ids = Object.keys(index[key]||{});
|
||||
for (j=0;j<ids.length;j++) {
|
||||
var node = index[key][ids[j]];
|
||||
var isConfigNode = node.node._def.category === "config" && node.node.type !== 'group';
|
||||
@@ -247,7 +247,7 @@ RED.search = (function() {
|
||||
}
|
||||
currentResults = search(value);
|
||||
if (currentResults.length > 0) {
|
||||
for (i=0;i<Math.min(currentResults.length,25);i++) {
|
||||
for (let i=0;i<Math.min(currentResults.length,25);i++) {
|
||||
searchResults.editableList('addItem',currentResults[i])
|
||||
}
|
||||
if (currentResults.length > 25) {
|
||||
|
@@ -983,6 +983,17 @@ RED.subflow = (function() {
|
||||
default: inputType
|
||||
})
|
||||
input.typedInput('value',val.value)
|
||||
if (inputType === 'cred') {
|
||||
if (node.credentials) {
|
||||
if (node.credentials[tenv.name]) {
|
||||
input.typedInput('value', node.credentials[tenv.name]);
|
||||
} else if (node.credentials['has_'+tenv.name]) {
|
||||
input.typedInput('value', "__PWRD__")
|
||||
} else {
|
||||
input.typedInput('value', "");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
input.val(val.value)
|
||||
}
|
||||
|
@@ -224,6 +224,7 @@ RED.view = (function() {
|
||||
.on("mousedown", canvasMouseDown)
|
||||
.on("mouseup", canvasMouseUp)
|
||||
.on("mouseenter", function() {
|
||||
d3.select(document).on('mouseup.red-ui-workspace-tracker', null)
|
||||
if (lasso) {
|
||||
if (d3.event.buttons !== 1) {
|
||||
lasso.remove();
|
||||
@@ -239,6 +240,7 @@ RED.view = (function() {
|
||||
}
|
||||
}
|
||||
})
|
||||
.on("mouseleave", canvasMouseLeave)
|
||||
.on("touchend", function() {
|
||||
d3.event.preventDefault();
|
||||
clearTimeout(touchStartTime);
|
||||
@@ -378,6 +380,9 @@ RED.view = (function() {
|
||||
drag_lines = [];
|
||||
|
||||
RED.events.on("workspace:change",function(event) {
|
||||
// Just in case the mouse left the workspace whilst doing an action,
|
||||
// put us back into default mode so the refresh works
|
||||
mouse_mode = 0
|
||||
if (event.old !== 0) {
|
||||
workspaceScrollPositions[event.old] = {
|
||||
left:chart.scrollLeft(),
|
||||
@@ -1741,10 +1746,19 @@ RED.view = (function() {
|
||||
redraw();
|
||||
}
|
||||
}
|
||||
|
||||
function canvasMouseLeave() {
|
||||
if (mouse_mode !== 0 && d3.event.buttons !== 0) {
|
||||
d3.select(document).on('mouseup.red-ui-workspace-tracker', function() {
|
||||
d3.select(document).on('mouseup.red-ui-workspace-tracker', null)
|
||||
canvasMouseUp.call(this)
|
||||
})
|
||||
}
|
||||
}
|
||||
function canvasMouseUp() {
|
||||
lastClickPosition = [d3.event.offsetX/scaleFactor,d3.event.offsetY/scaleFactor];
|
||||
if (RED.view.DEBUG) { console.warn("canvasMouseUp", mouse_mode); }
|
||||
if (RED.view.DEBUG) {
|
||||
console.warn("canvasMouseUp", { mouse_mode, point: d3.mouse(this), event: d3.event });
|
||||
}
|
||||
var i;
|
||||
var historyEvent;
|
||||
if (mouse_mode === RED.state.PANNING) {
|
||||
@@ -3588,6 +3602,9 @@ RED.view = (function() {
|
||||
function portMouseOutProxy(e) { portMouseOut(d3.select(this), this.__data__,this.__portType__,this.__portIndex__, e); }
|
||||
|
||||
function linkMouseDown(d) {
|
||||
if (RED.view.DEBUG) {
|
||||
console.warn("linkMouseDown", { mouse_mode, point: d3.mouse(this), event: d3.event });
|
||||
}
|
||||
if (mouse_mode === RED.state.SELECTING_NODE) {
|
||||
d3.event.stopPropagation();
|
||||
return;
|
||||
|
@@ -281,9 +281,22 @@ RED.workspaces = (function() {
|
||||
onselect: "core:show-last-hidden-flow"
|
||||
}
|
||||
]
|
||||
if (hideStack.length > 0) {
|
||||
let hiddenFlows = new Set()
|
||||
for (let i = 0; i < hideStack.length; i++) {
|
||||
let ids = hideStack[i]
|
||||
if (!Array.isArray(ids)) {
|
||||
ids = [ids]
|
||||
}
|
||||
ids.forEach(id => {
|
||||
if (RED.nodes.workspace(id)) {
|
||||
hiddenFlows.add(id)
|
||||
}
|
||||
})
|
||||
}
|
||||
const flowCount = hiddenFlows.size;
|
||||
if (flowCount > 0) {
|
||||
menuItems.unshift({
|
||||
label: RED._("workspace.hiddenFlows",{count: hideStack.length}),
|
||||
label: RED._("workspace.hiddenFlows",{count: flowCount}),
|
||||
onselect: "core:list-hidden-flows"
|
||||
})
|
||||
}
|
||||
|
@@ -27,10 +27,10 @@
|
||||
}
|
||||
|
||||
.ui-widget.ui-widget-content {
|
||||
border: 1px solid $tertiary-border-color;
|
||||
border: 1px solid $tertiary-border-color;
|
||||
}
|
||||
.ui-widget-content {
|
||||
border: 1px solid $secondary-border-color;
|
||||
border: 1px solid $secondary-border-color;
|
||||
}
|
||||
|
||||
.ui-widget-header {
|
||||
|
@@ -254,7 +254,7 @@ button.red-ui-palette-editor-upload-button {
|
||||
padding: 2px 8px;
|
||||
}
|
||||
form {
|
||||
width: 0;
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
.red-ui-palette-editor-upload {
|
||||
|
@@ -109,9 +109,10 @@ module.exports = function(RED) {
|
||||
if (!property) return;
|
||||
|
||||
if (valueType === "jsonata") {
|
||||
if (p.exp) {
|
||||
if (p.v) {
|
||||
try {
|
||||
var val = RED.util.evaluateJSONataExpression(p.exp, msg);
|
||||
var exp = RED.util.prepareJSONataExpression(p.v, node);
|
||||
var val = RED.util.evaluateJSONataExpression(exp, msg);
|
||||
RED.util.setMessageProperty(msg, property, val, true);
|
||||
}
|
||||
catch (err) {
|
||||
|
@@ -399,7 +399,7 @@
|
||||
$("#func-tabs-content").children().hide();
|
||||
$("#" + tab.id).show();
|
||||
let editor = $("#" + tab.id).find('.monaco-editor').first();
|
||||
if(editor.length) {
|
||||
if(editor.length) {
|
||||
if(that.editor.nodered && that.editor.type == "monaco") {
|
||||
that.editor.nodered.refreshModuleLibs(getLibsList());
|
||||
}
|
||||
|
@@ -168,9 +168,9 @@ module.exports = function(RED) {
|
||||
return getFromValueType(RED.util.getMessageProperty(msg,rule.from),done);
|
||||
} else if (rule.fromt === 'flow' || rule.fromt === 'global') {
|
||||
var contextKey = RED.util.parseContextStore(rule.from);
|
||||
if (/\[msg\./.test(context.key)) {
|
||||
if (/\[msg\./.test(contextKey.key)) {
|
||||
// The key has a nest msg. reference to evaluate first
|
||||
context.key = RED.util.normalisePropertyExpression(contextKey.key,msg,true);
|
||||
contextKey.key = RED.util.normalisePropertyExpression(contextKey.key,msg,true);
|
||||
}
|
||||
node.context()[rule.fromt].get(contextKey.key, contextKey.store, (err,fromValue) => {
|
||||
if (err) {
|
||||
|
@@ -275,18 +275,22 @@ module.exports = function(RED) {
|
||||
if (msg.hasOwnProperty("flush")) {
|
||||
var len = node.buffer.length;
|
||||
if (typeof(msg.flush) == 'number') { len = Math.min(Math.floor(msg.flush),len); }
|
||||
while (len > 0) {
|
||||
const msgInfo = node.buffer.shift();
|
||||
if (Object.keys(msgInfo.msg).length > 1) {
|
||||
node.send(msgInfo.msg);
|
||||
msgInfo.done();
|
||||
}
|
||||
len = len - 1;
|
||||
}
|
||||
if (node.buffer.length === 0) {
|
||||
if (len === 0) {
|
||||
clearInterval(node.intervalID);
|
||||
node.intervalID = -1;
|
||||
}
|
||||
else {
|
||||
while (len > 0) {
|
||||
const msgInfo = node.buffer.shift();
|
||||
if (Object.keys(msgInfo.msg).length > 1) {
|
||||
node.send(msgInfo.msg);
|
||||
msgInfo.done();
|
||||
}
|
||||
len = len - 1;
|
||||
}
|
||||
clearInterval(node.intervalID);
|
||||
node.intervalID = setInterval(sendMsgFromBuffer, node.rate);
|
||||
}
|
||||
node.status({fill:"blue",shape:"dot",text:node.buffer.length});
|
||||
done();
|
||||
}
|
||||
|
@@ -442,7 +442,17 @@
|
||||
}
|
||||
return defaultContentType || 'none'
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a topic string is valid for publishing
|
||||
* @param {string} topic
|
||||
* @returns `true` if it is a valid topic
|
||||
*/
|
||||
function validateMQTTPublishTopic(topic, opts) {
|
||||
if(!topic || topic == "" || !/[\+#\b\f\n\r\t\v\0]/.test(topic)) {
|
||||
return true;
|
||||
}
|
||||
return RED._("node-red:mqtt.errors.invalid-topic");
|
||||
}
|
||||
RED.nodes.registerType('mqtt-broker',{
|
||||
category: 'config',
|
||||
defaults: {
|
||||
|
@@ -68,12 +68,21 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a topic string is valid
|
||||
* Test a topic string is valid for subscription
|
||||
* @param {string} topic
|
||||
* @returns `true` if it is a valid topic
|
||||
*/
|
||||
function isValidSubscriptionTopic(topic) {
|
||||
return /^(#$|(\+|[^+#]*)(\/(\+|[^+#]*))*(\/(\+|#|[^+#]*))?$)/.test(topic)
|
||||
return /^(#$|(\+|[^+#]*)(\/(\+|[^+#]*))*(\/(\+|#|[^+#]*))?$)/.test(topic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a topic string is valid for publishing
|
||||
* @param {string} topic
|
||||
* @returns `true` if it is a valid topic
|
||||
*/
|
||||
function isValidPublishTopic(topic) {
|
||||
return !/[\+#\b\f\n\r\t\v\0]/.test(topic);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -288,7 +297,7 @@ module.exports = function(RED) {
|
||||
//TODO: delete msg.responseTopic - to prevent it being resent?
|
||||
}
|
||||
}
|
||||
topicOK = topicOK && !/[\+#\b\f\n\r\t\v\0]/.test(msg.topic);
|
||||
topicOK = topicOK && isValidPublishTopic(msg.topic);
|
||||
|
||||
if (topicOK) {
|
||||
node.brokerConn.publish(msg, done); // send the message
|
||||
@@ -362,7 +371,7 @@ module.exports = function(RED) {
|
||||
node.brokerConn.connect(function () {
|
||||
done();
|
||||
});
|
||||
}, true)
|
||||
})
|
||||
} else {
|
||||
// Without force flag, we will refuse to cycle an active connection
|
||||
done(new Error(RED._('mqtt.errors.invalid-action-alreadyconnected')));
|
||||
@@ -391,6 +400,7 @@ module.exports = function(RED) {
|
||||
node.options = {};
|
||||
node.queue = [];
|
||||
node.subscriptions = {};
|
||||
node.clientListeners = []
|
||||
/** @type {mqtt.MqttClient}*/ this.client;
|
||||
node.setOptions = function(opts, init) {
|
||||
if(!opts || typeof opts !== "object") {
|
||||
@@ -529,7 +539,7 @@ module.exports = function(RED) {
|
||||
// Only for ws or wss, check if proxy env var for additional configuration
|
||||
if (node.brokerurl.indexOf("wss://") > -1 || node.brokerurl.indexOf("ws://") > -1) {
|
||||
// check if proxy is set in env
|
||||
let prox, noprox;
|
||||
let prox, noprox, noproxy;
|
||||
if (process.env.http_proxy) { prox = process.env.http_proxy; }
|
||||
if (process.env.HTTP_PROXY) { prox = process.env.HTTP_PROXY; }
|
||||
if (process.env.no_proxy) { noprox = process.env.no_proxy.split(","); }
|
||||
@@ -656,11 +666,16 @@ module.exports = function(RED) {
|
||||
setStatusConnecting(node, true);
|
||||
try {
|
||||
node.serverProperties = {};
|
||||
if(node.client) {
|
||||
//belt and braces to avoid left over clients
|
||||
node.client.end(true);
|
||||
node._clientRemoveListeners();
|
||||
}
|
||||
node.client = mqtt.connect(node.brokerurl, node.options);
|
||||
node.client.setMaxListeners(0);
|
||||
let callbackDone = false; //prevent re-connects causing node.client.on('connect' firing callback multiple times
|
||||
let callbackDone = false; //prevent re-connects causing node._clientOn('connect' firing callback multiple times
|
||||
// Register successful connect or reconnect handler
|
||||
node.client.on('connect', function (connack) {
|
||||
node._clientOn('connect', function (connack) {
|
||||
node.closing = false;
|
||||
node.connecting = false;
|
||||
node.connected = true;
|
||||
@@ -692,7 +707,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
setStatusConnected(node, true);
|
||||
// Remove any existing listeners before resubscribing to avoid duplicates in the event of a re-connection
|
||||
node.client.removeAllListeners('message');
|
||||
node._clientRemoveListeners('message');
|
||||
|
||||
// Re-subscribe to stored topics
|
||||
for (var s in node.subscriptions) {
|
||||
@@ -704,7 +719,7 @@ module.exports = function(RED) {
|
||||
if (node.subscriptions[s].hasOwnProperty(r)) {
|
||||
qos = Math.max(qos,node.subscriptions[s][r].qos);
|
||||
_options = node.subscriptions[s][r].options;
|
||||
node.client.on('message',node.subscriptions[s][r].handler);
|
||||
node._clientOn('message',node.subscriptions[s][r].handler);
|
||||
}
|
||||
}
|
||||
_options.qos = _options.qos || qos;
|
||||
@@ -717,11 +732,11 @@ module.exports = function(RED) {
|
||||
node.publish(node.birthMessage);
|
||||
}
|
||||
});
|
||||
node.client.on("reconnect", function() {
|
||||
node._clientOn("reconnect", function() {
|
||||
setStatusConnecting(node, true);
|
||||
});
|
||||
//Broker Disconnect - V5 event
|
||||
node.client.on("disconnect", function(packet) {
|
||||
node._clientOn("disconnect", function(packet) {
|
||||
//Emitted after receiving disconnect packet from broker. MQTT 5.0 feature.
|
||||
const rc = (packet && packet.properties && packet.reasonCode) || packet.reasonCode;
|
||||
const rs = packet && packet.properties && packet.properties.reasonString || "";
|
||||
@@ -735,7 +750,7 @@ module.exports = function(RED) {
|
||||
setStatusDisconnected(node, true);
|
||||
});
|
||||
// Register disconnect handlers
|
||||
node.client.on('close', function () {
|
||||
node._clientOn('close', function () {
|
||||
if (node.connected) {
|
||||
node.connected = false;
|
||||
node.log(RED._("mqtt.state.disconnected",{broker:(node.clientid?node.clientid+"@":"")+node.brokerurl}));
|
||||
@@ -747,42 +762,59 @@ module.exports = function(RED) {
|
||||
|
||||
// Register connect error handler
|
||||
// The client's own reconnect logic will take care of errors
|
||||
node.client.on('error', function (error) {
|
||||
node._clientOn('error', function (error) {
|
||||
});
|
||||
}catch(err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
node.disconnect = function (callback, force) {
|
||||
const _callback = function (resetNodeConnectedState, _force) {
|
||||
setStatusDisconnected(node, true);
|
||||
if(resetNodeConnectedState || _force) {
|
||||
node.client.removeAllListeners();
|
||||
node.closing = true;
|
||||
node.connecting = false;
|
||||
node.connected = false;
|
||||
|
||||
node.disconnect = function (callback) {
|
||||
const _callback = function () {
|
||||
if(node.connected || node.connecting) {
|
||||
setStatusDisconnected(node, true);
|
||||
}
|
||||
if(node.client) { node._clientRemoveListeners(); }
|
||||
node.connecting = false;
|
||||
node.connected = false;
|
||||
callback && typeof callback == "function" && callback();
|
||||
};
|
||||
if(node.closing) {
|
||||
return _callback(false, force);
|
||||
}
|
||||
var endCallBack = function endCallBack() {
|
||||
}
|
||||
if(!node.client) { return _callback(); }
|
||||
if(node.closing) { return _callback(); }
|
||||
|
||||
let waitEnd = (client, ms) => {
|
||||
return new Promise( (resolve, reject) => {
|
||||
node.closing = true;
|
||||
if(!client) {
|
||||
resolve();
|
||||
} else {
|
||||
const t = setTimeout(() => {
|
||||
//clean end() has exceeded WAIT_END, lets force end!
|
||||
client && client.end(true);
|
||||
reject();
|
||||
}, ms);
|
||||
client.end(() => {
|
||||
clearTimeout(t);
|
||||
resolve()
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
if(node.connected && node.closeMessage) {
|
||||
node.publish(node.closeMessage, function (err) {
|
||||
node.client.end(endCallBack);
|
||||
_callback(true, force);
|
||||
waitEnd(node.client, 2000).then(() => {
|
||||
_callback();
|
||||
}).catch((e) => {
|
||||
_callback();
|
||||
})
|
||||
});
|
||||
} else if(node.connected) {
|
||||
node.client.end(endCallBack);
|
||||
_callback(true, force);
|
||||
} else if(node.connecting) {
|
||||
node.client.end();
|
||||
_callback(true, true);
|
||||
} else {
|
||||
_callback(false, force);
|
||||
waitEnd(node.client, 2000).then(() => {
|
||||
_callback();
|
||||
}).catch((e) => {
|
||||
_callback();
|
||||
})
|
||||
}
|
||||
}
|
||||
node.subscriptionIds = {};
|
||||
@@ -819,7 +851,7 @@ module.exports = function(RED) {
|
||||
};
|
||||
node.subscriptions[topic][ref] = sub;
|
||||
if (node.connected) {
|
||||
node.client.on('message',sub.handler);
|
||||
node._clientOn('message',sub.handler);
|
||||
node.client.subscribe(topic, options);
|
||||
}
|
||||
};
|
||||
@@ -830,7 +862,7 @@ module.exports = function(RED) {
|
||||
if (sub) {
|
||||
if (sub[ref]) {
|
||||
if(node.client) {
|
||||
node.client.removeListener('message',sub[ref].handler);
|
||||
node._clientRemoveListeners('message',sub[ref].handler);
|
||||
}
|
||||
delete sub[ref];
|
||||
}
|
||||
@@ -864,8 +896,18 @@ module.exports = function(RED) {
|
||||
qos: msg.qos || 0,
|
||||
retain: msg.retain || false
|
||||
};
|
||||
let topicOK = hasProperty(msg, "topic") && (typeof msg.topic === "string") && (isValidPublishTopic(msg.topic));
|
||||
//https://github.com/mqttjs/MQTT.js/blob/master/README.md#mqttclientpublishtopic-message-options-callback
|
||||
if(node.options.protocolVersion == 5) {
|
||||
const bsp = node.serverProperties || {};
|
||||
if (msg.userProperties && typeof msg.userProperties !== "object") {
|
||||
delete msg.userProperties;
|
||||
}
|
||||
if (hasProperty(msg, "topicAlias") && !isNaN(Number(msg.topicAlias))) {
|
||||
msg.topicAlias = parseInt(msg.topicAlias);
|
||||
} else {
|
||||
delete msg.topicAlias;
|
||||
}
|
||||
options.properties = options.properties || {};
|
||||
setStrProp(msg, options.properties, "responseTopic");
|
||||
setBufferProp(msg, options.properties, "correlationData");
|
||||
@@ -875,31 +917,75 @@ module.exports = function(RED) {
|
||||
setIntProp(msg, options.properties, "topicAlias", 1, node.serverProperties.topicAliasMaximum || 0);
|
||||
setBoolProp(msg, options.properties, "payloadFormatIndicator");
|
||||
//FUTURE setIntProp(msg, options.properties, "subscriptionIdentifier", 1, 268435455);
|
||||
if (options.properties.topicAlias) {
|
||||
if (!node.topicAliases.hasOwnProperty(options.properties.topicAlias) && msg.topic == "") {
|
||||
|
||||
//check & sanitise topic
|
||||
if (topicOK && options.properties.topicAlias) {
|
||||
let aliasValid = (bsp.topicAliasMaximum && bsp.topicAliasMaximum >= options.properties.topicAlias);
|
||||
if (!aliasValid) {
|
||||
done("Invalid topicAlias");
|
||||
return
|
||||
}
|
||||
if (node.topicAliases[options.properties.topicAlias] === msg.topic) {
|
||||
msg.topic = ""
|
||||
msg.topic = "";
|
||||
} else {
|
||||
node.topicAliases[options.properties.topicAlias] = msg.topic
|
||||
node.topicAliases[options.properties.topicAlias] = msg.topic;
|
||||
}
|
||||
} else if (!msg.topic && options.properties.responseTopic) {
|
||||
msg.topic = msg.responseTopic;
|
||||
topicOK = isValidPublishTopic(msg.topic);
|
||||
delete msg.responseTopic; //prevent responseTopic being resent?
|
||||
}
|
||||
}
|
||||
|
||||
node.client.publish(msg.topic, msg.payload, options, function(err) {
|
||||
done && done(err);
|
||||
return
|
||||
});
|
||||
if (topicOK) {
|
||||
node.client.publish(msg.topic, msg.payload, options, function(err) {
|
||||
done && done(err);
|
||||
return
|
||||
});
|
||||
} else {
|
||||
const error = new Error(RED._("mqtt.errors.invalid-topic"));
|
||||
error.warn = true;
|
||||
done(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
node.on('close', function(done) {
|
||||
node.closing = true;
|
||||
node.disconnect(done);
|
||||
node.disconnect(function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Add event handlers to the MQTT.js client and track them so that
|
||||
* we do not remove any handlers that the MQTT client uses internally.
|
||||
* Use {@link node._clientRemoveListeners `node._clientRemoveListeners`} to remove handlers
|
||||
* @param {string} event The name of the event
|
||||
* @param {function} handler The handler for this event
|
||||
*/
|
||||
node._clientOn = function(event, handler) {
|
||||
node.clientListeners.push({event, handler})
|
||||
node.client.on(event, handler)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove event handlers from the MQTT.js client & only the events
|
||||
* that we attached in {@link node._clientOn `node._clientOn`}.
|
||||
* * If `event` is omitted, then all events matching `handler` are removed
|
||||
* * If `handler` is omitted, then all events named `event` are removed
|
||||
* * If both parameters are omitted, then all events are removed
|
||||
* @param {string} [event] The name of the event (optional)
|
||||
* @param {function} [handler] The handler for this event (optional)
|
||||
*/
|
||||
node._clientRemoveListeners = function(event, handler) {
|
||||
node.clientListeners = node.clientListeners.filter((l) => {
|
||||
if (event && event !== l.event) { return true; }
|
||||
if (handler && handler !== l.handler) { return true; }
|
||||
node.client.removeListener(l.event, l.handler)
|
||||
return false; //found and removed, filter out this one
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RED.nodes.registerType("mqtt-broker",MQTTBrokerNode,{
|
||||
@@ -1074,6 +1160,7 @@ module.exports = function(RED) {
|
||||
node.brokerConn.unsubscribe(node.topic,node.id, removed);
|
||||
}
|
||||
node.brokerConn.deregister(node, done);
|
||||
node.brokerConn = null;
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
@@ -1138,6 +1225,7 @@ module.exports = function(RED) {
|
||||
node.on('close', function(done) {
|
||||
if (node.brokerConn) {
|
||||
node.brokerConn.deregister(node,done);
|
||||
node.brokerConn = null;
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
|
@@ -91,6 +91,11 @@
|
||||
<label for="node-input-senderr" style="width: auto" data-i18n="httpin.senderr"></label>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<input type="checkbox" id="node-input-insecureHTTPParser" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-input-insecureHTTPParser", style="width: auto;" data-i18n="httpin.insecureHTTPParser"></label>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-input-ret"><i class="fa fa-arrow-left"></i> <span data-i18n="httpin.label.return"></span></label>
|
||||
@@ -120,6 +125,7 @@
|
||||
tls: {type:"tls-config",required: false},
|
||||
persist: {value:false},
|
||||
proxy: {type:"http proxy",required: false},
|
||||
insecureHTTPParser: {value: false},
|
||||
authType: {value: ""},
|
||||
senderr: {value: false}
|
||||
},
|
||||
@@ -224,6 +230,12 @@
|
||||
} else {
|
||||
$("#node-input-useProxy").prop("checked", false);
|
||||
}
|
||||
|
||||
if (node.insecureHTTPParser) {
|
||||
$("node-intput-insecureHTTPParser").prop("checked", true)
|
||||
} else {
|
||||
$("node-intput-insecureHTTPParser").prop("checked", false)
|
||||
}
|
||||
updateProxyOptions();
|
||||
$("#node-input-useProxy").on("click", function() {
|
||||
updateProxyOptions();
|
||||
|
@@ -214,6 +214,10 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
||||
delete options.headers[h];
|
||||
}
|
||||
})
|
||||
|
||||
if (node.insecureHTTPParser) {
|
||||
options.insecureHTTPParser = true
|
||||
}
|
||||
}
|
||||
],
|
||||
beforeRedirect: [
|
||||
|
@@ -432,7 +432,7 @@ module.exports = function(RED) {
|
||||
});
|
||||
}
|
||||
else {
|
||||
var connectedSockets = [];
|
||||
const connectedSockets = new Set();
|
||||
node.status({text:RED._("tcpin.status.connections",{count:0})});
|
||||
let srv = net;
|
||||
let connOpts;
|
||||
@@ -453,16 +453,16 @@ module.exports = function(RED) {
|
||||
});
|
||||
socket.on('close',function() {
|
||||
node.log(RED._("tcpin.status.connection-closed",{host:socket.remoteAddress, port:socket.remotePort}));
|
||||
connectedSockets.splice(connectedSockets.indexOf(socket),1);
|
||||
node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})});
|
||||
connectedSockets.delete(socket);
|
||||
node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.size})});
|
||||
});
|
||||
socket.on('error',function() {
|
||||
node.log(RED._("tcpin.errors.socket-error",{host:socket.remoteAddress, port:socket.remotePort}));
|
||||
connectedSockets.splice(connectedSockets.indexOf(socket),1);
|
||||
node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})});
|
||||
connectedSockets.delete(socket);
|
||||
node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.size})});
|
||||
});
|
||||
connectedSockets.push(socket);
|
||||
node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})});
|
||||
connectedSockets.add(socket);
|
||||
node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.size})});
|
||||
});
|
||||
|
||||
node.on("input", function(msg, nodeSend, nodeDone) {
|
||||
@@ -475,10 +475,10 @@ module.exports = function(RED) {
|
||||
} else {
|
||||
buffer = Buffer.from(""+msg.payload);
|
||||
}
|
||||
for (var i = 0; i < connectedSockets.length; i += 1) {
|
||||
if (node.doend === true) { connectedSockets[i].end(buffer); }
|
||||
else { connectedSockets[i].write(buffer); }
|
||||
}
|
||||
connectedSockets.forEach(soc => {
|
||||
if (node.doend === true) { soc.end(buffer); }
|
||||
else { soc.write(buffer); }
|
||||
})
|
||||
}
|
||||
nodeDone();
|
||||
});
|
||||
@@ -495,12 +495,10 @@ module.exports = function(RED) {
|
||||
} else {
|
||||
node.log(RED._("tcpin.status.listening-port",{port:node.port}));
|
||||
node.on('close', function() {
|
||||
for (var c in connectedSockets) {
|
||||
if (connectedSockets.hasOwnProperty(c)) {
|
||||
connectedSockets[c].end();
|
||||
connectedSockets[c].unref();
|
||||
}
|
||||
}
|
||||
connectedSockets.forEach(soc => {
|
||||
soc.end();
|
||||
soc.unref();
|
||||
})
|
||||
server.close();
|
||||
node.log(RED._("tcpin.status.stopped-listening",{port:node.port}));
|
||||
});
|
||||
|
@@ -89,6 +89,9 @@ module.exports = function(RED) {
|
||||
else if (msg.payload[s][t].toString().indexOf(node.sep) !== -1) { // add quotes if any "commas"
|
||||
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
|
||||
}
|
||||
else if (msg.payload[s][t].toString().indexOf("\n") !== -1) { // add quotes if any "\n"
|
||||
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
|
||||
}
|
||||
}
|
||||
ou += msg.payload[s].join(node.sep) + node.ret;
|
||||
}
|
||||
@@ -112,7 +115,7 @@ module.exports = function(RED) {
|
||||
q = q.replace(/"/g, '""');
|
||||
ou += node.quo + q + node.quo + node.sep;
|
||||
}
|
||||
else if (q.indexOf(node.sep) !== -1) { // add quotes if any "commas"
|
||||
else if (q.indexOf(node.sep) !== -1 || p.indexOf("\n") !== -1) { // add quotes if any "commas" or "\n"
|
||||
ou += node.quo + q + node.quo + node.sep;
|
||||
}
|
||||
else { ou += q + node.sep; } // otherwise just add
|
||||
@@ -134,7 +137,7 @@ module.exports = function(RED) {
|
||||
p = p.replace(/"/g, '""');
|
||||
ou += node.quo + p + node.quo + node.sep;
|
||||
}
|
||||
else if (p.indexOf(node.sep) !== -1) { // add quotes if any "commas"
|
||||
else if (p.indexOf(node.sep) !== -1 || p.indexOf("\n") !== -1) { // add quotes if any "commas" or "\n"
|
||||
ou += node.quo + p + node.quo + node.sep;
|
||||
}
|
||||
else { ou += p + node.sep; } // otherwise just add
|
||||
|
@@ -314,11 +314,13 @@ module.exports = function(RED) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
msgInfo.send({payload: result});
|
||||
msgInfo.msg.payload = result;
|
||||
msgInfo.send(msgInfo.msg);
|
||||
done();
|
||||
});
|
||||
} else {
|
||||
msgInfo.send({payload: result});
|
||||
msgInfo.msg.payload = result;
|
||||
msgInfo.send(msgInfo.msg);
|
||||
done();
|
||||
}
|
||||
} else {
|
||||
|
@@ -47,7 +47,7 @@
|
||||
<div class="form-row">
|
||||
<input type="checkbox" id="node-input-allowEmptySequence" style="margin-left:20px; margin-right: 10px; vertical-align:top; width:auto;">
|
||||
<label for="node-input-allowEmptySequence" style="width:auto;" data-i18n="batch.interval.empty"></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="node-row-msg-concat">
|
||||
|
149
packages/node_modules/@node-red/nodes/examples/function/delay/06 - Simple Queue with release
vendored
Normal file
149
packages/node_modules/@node-red/nodes/examples/function/delay/06 - Simple Queue with release
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
[
|
||||
{
|
||||
"id": "48d660b3a4109400",
|
||||
"type": "inject",
|
||||
"z": "9e5f48c16729e4f0",
|
||||
"name": "inject",
|
||||
"props": [
|
||||
{
|
||||
"p": "payload"
|
||||
}
|
||||
],
|
||||
"repeat": "",
|
||||
"crontab": "",
|
||||
"once": false,
|
||||
"onceDelay": 0.1,
|
||||
"topic": "",
|
||||
"payload": "",
|
||||
"payloadType": "date",
|
||||
"x": 185,
|
||||
"y": 795,
|
||||
"wires": [
|
||||
[
|
||||
"e0f9e206681f3504"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "e0f9e206681f3504",
|
||||
"type": "delay",
|
||||
"z": "9e5f48c16729e4f0",
|
||||
"name": "",
|
||||
"pauseType": "rate",
|
||||
"timeout": "5",
|
||||
"timeoutUnits": "seconds",
|
||||
"rate": "1",
|
||||
"nbRateUnits": "30",
|
||||
"rateUnits": "second",
|
||||
"randomFirst": "1",
|
||||
"randomLast": "5",
|
||||
"randomUnits": "seconds",
|
||||
"drop": false,
|
||||
"allowrate": false,
|
||||
"outputs": 1,
|
||||
"x": 430,
|
||||
"y": 795,
|
||||
"wires": [
|
||||
[
|
||||
"e470f1d794e1bef9",
|
||||
"af7cea1dfb797a75"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "943543cf7a1958e4",
|
||||
"type": "change",
|
||||
"z": "9e5f48c16729e4f0",
|
||||
"name": "set flush to 1",
|
||||
"rules": [
|
||||
{
|
||||
"t": "set",
|
||||
"p": "flush",
|
||||
"pt": "msg",
|
||||
"to": "1",
|
||||
"tot": "num"
|
||||
},
|
||||
{
|
||||
"t": "delete",
|
||||
"p": "payload",
|
||||
"pt": "msg"
|
||||
}
|
||||
],
|
||||
"action": "",
|
||||
"property": "",
|
||||
"from": "",
|
||||
"to": "",
|
||||
"reg": false,
|
||||
"x": 510,
|
||||
"y": 915,
|
||||
"wires": [
|
||||
[
|
||||
"e0f9e206681f3504"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "e470f1d794e1bef9",
|
||||
"type": "function",
|
||||
"z": "9e5f48c16729e4f0",
|
||||
"name": "Do something that takes a few seconds",
|
||||
"func": "\n//send on the message between 3 and 6 seconds later\nsetTimeout(\n function() { \n node.send(msg) \n }, \n Math.random() * 3000 + 3000\n);\nreturn null;",
|
||||
"outputs": 1,
|
||||
"noerr": 0,
|
||||
"initialize": "",
|
||||
"finalize": "",
|
||||
"libs": [],
|
||||
"x": 760,
|
||||
"y": 795,
|
||||
"wires": [
|
||||
[
|
||||
"943543cf7a1958e4",
|
||||
"859258551b8389b7"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "af7cea1dfb797a75",
|
||||
"type": "debug",
|
||||
"z": "9e5f48c16729e4f0",
|
||||
"name": "IN",
|
||||
"active": true,
|
||||
"tosidebar": true,
|
||||
"console": false,
|
||||
"tostatus": false,
|
||||
"complete": "payload",
|
||||
"targetType": "msg",
|
||||
"statusVal": "",
|
||||
"statusType": "auto",
|
||||
"x": 710,
|
||||
"y": 735,
|
||||
"wires": []
|
||||
},
|
||||
{
|
||||
"id": "859258551b8389b7",
|
||||
"type": "debug",
|
||||
"z": "9e5f48c16729e4f0",
|
||||
"name": "OUT",
|
||||
"active": true,
|
||||
"tosidebar": true,
|
||||
"console": false,
|
||||
"tostatus": false,
|
||||
"complete": "payload",
|
||||
"targetType": "msg",
|
||||
"statusVal": "",
|
||||
"statusType": "auto",
|
||||
"x": 895,
|
||||
"y": 735,
|
||||
"wires": []
|
||||
},
|
||||
{
|
||||
"id": "ecaaf26326da10ee",
|
||||
"type": "comment",
|
||||
"z": "9e5f48c16729e4f0",
|
||||
"name": "Simple Queue with release",
|
||||
"info": "This example shows how to use a delay node set to rate limit mode as a simple queue to feed a\nprocess that may take some time to complete. Once that process completes the feedback is then\nset to flush out the next message - thus running the \"loop\" as fast as possible with no overlaps.\n\n**Note**: only the `msg.flush` property msut be set - otherwise the other properties that are fed \nback will be added as another new message to the queue.",
|
||||
"x": 235,
|
||||
"y": 915,
|
||||
"wires": []
|
||||
}
|
||||
]
|
@@ -525,7 +525,8 @@
|
||||
},
|
||||
"status": {
|
||||
"requesting": "requesting"
|
||||
}
|
||||
},
|
||||
"insecureHTTPParser": "Disable strict HTTP parsing"
|
||||
},
|
||||
"websocket": {
|
||||
"label": {
|
||||
|
@@ -28,7 +28,7 @@
|
||||
<p>返却/sendの対象は次のとおりです:</p>
|
||||
<ul>
|
||||
<li>単一メッセージオブジェクト - 最初の出力に接続されたノードに渡されます</li>
|
||||
<li>メッセージオブジェクトの配列 - 対応する出力に接続されたノードに渡されます</li>
|
||||
<li>メッセージオブジェクトの配列 - 対応する出力に接続されたノードに渡されます</li>
|
||||
</ul>
|
||||
<p>注: 初期化処理の実行はノードの初期化中に行われます。そのため、初期化処理タブにsendを記述した場合に後続ノードでメッセージを受け取れないことがあります。</p>
|
||||
<p>配列要素が配列の場合には、複数のメッセージを対応する出力に送出します。</p>
|
||||
|
@@ -89,7 +89,7 @@
|
||||
<dt class="optional">userProperties <span class="property-type">オブジェクト</span></dt>
|
||||
<dd><b>MQTTv5</b>: メッセージのユーザプロパティ</dd>
|
||||
<dt class="optional">messageExpiryInterval <span class="property-type">数値</span></dt>
|
||||
<dd><b>MQTTv5</b>: 秒単位のメッセージの有効期限</dd>
|
||||
<dd><b>MQTTv5</b>: 秒単位のメッセージの有効期限</dd>
|
||||
<dt class="optional">topicAlias <span class="property-type">数値</span></dt>
|
||||
<dd><b>MQTTv5</b>: 使用するMQTTトピックエイリアス</dd>
|
||||
</dl>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/nodes",
|
||||
"version": "2.2.2",
|
||||
"version": "2.2.3",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@@ -11,6 +11,7 @@ const exec = require("@node-red/util").exec;
|
||||
const log = require("@node-red/util").log;
|
||||
const hooks = require("@node-red/util").hooks;
|
||||
const url = require("url");
|
||||
const { createRequire } = require("module");
|
||||
|
||||
const BUILTIN_MODULES = require('module').builtinModules;
|
||||
|
||||
@@ -139,10 +140,15 @@ function importModule(module) {
|
||||
}
|
||||
const externalModuleDir = getInstallDir();
|
||||
const moduleDir = path.join(externalModuleDir,"node_modules",module);
|
||||
// To handle both CJS and ESM we need to resolve the module to the
|
||||
// specific file that is loaded when the module is required/imported
|
||||
// As this won't be on the natural module search path, we use createRequire
|
||||
// to access the module
|
||||
const modulePath = createRequire(moduleDir).resolve(module)
|
||||
// Import needs the full path to the module's main .js file
|
||||
// It also needs to be a file:// url for Windows
|
||||
const moduleFile = url.pathToFileURL(require.resolve(moduleDir));
|
||||
return import(moduleFile);
|
||||
const moduleUrl = url.pathToFileURL(modulePath);
|
||||
return import(moduleUrl);
|
||||
}
|
||||
|
||||
function parseModuleName(module) {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/registry",
|
||||
"version": "2.2.2",
|
||||
"version": "2.2.3",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -16,7 +16,7 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/util": "2.2.2",
|
||||
"@node-red/util": "2.2.3",
|
||||
"clone": "2.1.2",
|
||||
"fs-extra": "10.0.0",
|
||||
"semver": "7.3.5",
|
||||
|
@@ -390,7 +390,6 @@ class Subflow extends Flow {
|
||||
}
|
||||
name = newName;
|
||||
}
|
||||
|
||||
|
||||
var parent = this.parent;
|
||||
if (parent) {
|
||||
|
@@ -373,11 +373,13 @@ var api = module.exports = {
|
||||
|
||||
}
|
||||
}
|
||||
for (cred in savedCredentials) {
|
||||
if (savedCredentials.hasOwnProperty(cred)) {
|
||||
if (!newCreds.hasOwnProperty(cred)) {
|
||||
delete savedCredentials[cred];
|
||||
dirty = true;
|
||||
if (/^subflow(:|$)/.test(nodeType)) {
|
||||
for (cred in savedCredentials) {
|
||||
if (savedCredentials.hasOwnProperty(cred)) {
|
||||
if (!newCreds.hasOwnProperty(cred)) {
|
||||
delete savedCredentials[cred];
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -100,7 +100,9 @@
|
||||
"error": "クレデンシャルの読み込みエラー: __message__",
|
||||
"error-saving": "クレデンシャルの保存エラー: __message__",
|
||||
"not-registered": "クレデンシャル '__type__' は登録されていません",
|
||||
"system-key-warning": "\n\n---------------------------------------------------------------------\nフローのクレデンシャルファイルはシステム生成キーで暗号化されています。\n\nシステム生成キーを何らかの理由で失った場合、クレデンシャルファイルを\n復元することはできません。その場合、ファイルを削除してクレデンシャルを\n再入力しなければなりません。\n\n設定ファイル内で 'credentialSecret' オプションを使って独自キーを設定\nします。変更を次にデプロイする際、Node-REDは選択したキーを用いてクレ\nデンシャルを再暗号化します。 \n\n---------------------------------------------------------------------\n"
|
||||
"system-key-warning": "\n\n---------------------------------------------------------------------\nフローのクレデンシャルファイルはシステム生成キーで暗号化されています。\n\nシステム生成キーを何らかの理由で失った場合、クレデンシャルファイルを\n復元することはできません。その場合、ファイルを削除してクレデンシャルを\n再入力しなければなりません。\n\n設定ファイル内で 'credentialSecret' オプションを使って独自キーを設定\nします。変更を次にデプロイする際、Node-REDは選択したキーを用いてクレ\nデンシャルを再暗号化します。 \n\n---------------------------------------------------------------------\n",
|
||||
"unencrypted": "暗号化されていないクレデンシャルを使用",
|
||||
"encryptedNotFound": "暗号化されたクレデンシャルが存在しません"
|
||||
},
|
||||
"flows": {
|
||||
"safe-mode": "セーフモードでフローを停止しました。開始するためにはデプロイしてください",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/runtime",
|
||||
"version": "2.2.2",
|
||||
"version": "2.2.3",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -16,8 +16,8 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/registry": "2.2.2",
|
||||
"@node-red/util": "2.2.2",
|
||||
"@node-red/registry": "2.2.3",
|
||||
"@node-red/util": "2.2.3",
|
||||
"async-mutex": "0.3.2",
|
||||
"clone": "2.1.2",
|
||||
"express": "4.17.2",
|
||||
|
@@ -641,7 +641,12 @@ function evaluateNodeProperty(value, type, node, msg, callback) {
|
||||
result = Date.now();
|
||||
} else if (type === 'bin') {
|
||||
var data = JSON.parse(value);
|
||||
result = Buffer.from(data);
|
||||
if (Array.isArray(data) || (typeof(data) === "string")) {
|
||||
result = Buffer.from(data);
|
||||
}
|
||||
else {
|
||||
throw createError("INVALID_BUFFER_DATA", "Not string or array");
|
||||
}
|
||||
} else if (type === 'msg' && msg) {
|
||||
try {
|
||||
result = getMessageProperty(msg,value);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/util",
|
||||
"version": "2.2.2",
|
||||
"version": "2.2.3",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
10
packages/node_modules/node-red/package.json
vendored
10
packages/node_modules/node-red/package.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red",
|
||||
"version": "2.2.2",
|
||||
"version": "2.2.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": "2.2.2",
|
||||
"@node-red/runtime": "2.2.2",
|
||||
"@node-red/util": "2.2.2",
|
||||
"@node-red/nodes": "2.2.2",
|
||||
"@node-red/editor-api": "2.2.3",
|
||||
"@node-red/runtime": "2.2.3",
|
||||
"@node-red/util": "2.2.3",
|
||||
"@node-red/nodes": "2.2.3",
|
||||
"basic-auth": "2.0.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"express": "4.17.2",
|
||||
|
140
packages/node_modules/node-red/settings.js
vendored
140
packages/node_modules/node-red/settings.js
vendored
@@ -230,80 +230,80 @@ module.exports = {
|
||||
* - externalModules
|
||||
******************************************************************************/
|
||||
|
||||
/** Uncomment the following to run node-red in your preferred language.
|
||||
* Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko
|
||||
* Some languages are more complete than others.
|
||||
*/
|
||||
// lang: "de",
|
||||
/** Uncomment the following to run node-red in your preferred language.
|
||||
* Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko
|
||||
* Some languages are more complete than others.
|
||||
*/
|
||||
// lang: "de",
|
||||
|
||||
/** Configure the logging output */
|
||||
logging: {
|
||||
/** Only console logging is currently supported */
|
||||
console: {
|
||||
/** Level of logging to be recorded. Options are:
|
||||
* fatal - only those errors which make the application unusable should be recorded
|
||||
* error - record errors which are deemed fatal for a particular request + fatal errors
|
||||
* warn - record problems which are non fatal + errors + fatal errors
|
||||
* info - record information about the general running of the application + warn + error + fatal errors
|
||||
* debug - record information which is more verbose than info + info + warn + error + fatal errors
|
||||
* trace - record very detailed logging + debug + info + warn + error + fatal errors
|
||||
* off - turn off all logging (doesn't affect metrics or audit)
|
||||
*/
|
||||
level: "info",
|
||||
/** Whether or not to include metric events in the log output */
|
||||
metrics: false,
|
||||
/** Whether or not to include audit events in the log output */
|
||||
audit: false
|
||||
}
|
||||
},
|
||||
/** Configure the logging output */
|
||||
logging: {
|
||||
/** Only console logging is currently supported */
|
||||
console: {
|
||||
/** Level of logging to be recorded. Options are:
|
||||
* fatal - only those errors which make the application unusable should be recorded
|
||||
* error - record errors which are deemed fatal for a particular request + fatal errors
|
||||
* warn - record problems which are non fatal + errors + fatal errors
|
||||
* info - record information about the general running of the application + warn + error + fatal errors
|
||||
* debug - record information which is more verbose than info + info + warn + error + fatal errors
|
||||
* trace - record very detailed logging + debug + info + warn + error + fatal errors
|
||||
* off - turn off all logging (doesn't affect metrics or audit)
|
||||
*/
|
||||
level: "info",
|
||||
/** Whether or not to include metric events in the log output */
|
||||
metrics: false,
|
||||
/** Whether or not to include audit events in the log output */
|
||||
audit: false
|
||||
}
|
||||
},
|
||||
|
||||
/** Context Storage
|
||||
* The following property can be used to enable context storage. The configuration
|
||||
* provided here will enable file-based context that flushes to disk every 30 seconds.
|
||||
* Refer to the documentation for further options: https://nodered.org/docs/api/context/
|
||||
*/
|
||||
//contextStorage: {
|
||||
// default: {
|
||||
// module:"localfilesystem"
|
||||
// },
|
||||
//},
|
||||
/** Context Storage
|
||||
* The following property can be used to enable context storage. The configuration
|
||||
* provided here will enable file-based context that flushes to disk every 30 seconds.
|
||||
* Refer to the documentation for further options: https://nodered.org/docs/api/context/
|
||||
*/
|
||||
//contextStorage: {
|
||||
// default: {
|
||||
// module:"localfilesystem"
|
||||
// },
|
||||
//},
|
||||
|
||||
/** `global.keys()` returns a list of all properties set in global context.
|
||||
* This allows them to be displayed in the Context Sidebar within the editor.
|
||||
* In some circumstances it is not desirable to expose them to the editor. The
|
||||
* following property can be used to hide any property set in `functionGlobalContext`
|
||||
* from being list by `global.keys()`.
|
||||
* By default, the property is set to false to avoid accidental exposure of
|
||||
* their values. Setting this to true will cause the keys to be listed.
|
||||
*/
|
||||
exportGlobalContextKeys: false,
|
||||
/** `global.keys()` returns a list of all properties set in global context.
|
||||
* This allows them to be displayed in the Context Sidebar within the editor.
|
||||
* In some circumstances it is not desirable to expose them to the editor. The
|
||||
* following property can be used to hide any property set in `functionGlobalContext`
|
||||
* from being list by `global.keys()`.
|
||||
* By default, the property is set to false to avoid accidental exposure of
|
||||
* their values. Setting this to true will cause the keys to be listed.
|
||||
*/
|
||||
exportGlobalContextKeys: false,
|
||||
|
||||
/** Configure how the runtime will handle external npm modules.
|
||||
* This covers:
|
||||
* - whether the editor will allow new node modules to be installed
|
||||
* - whether nodes, such as the Function node are allowed to have their
|
||||
* own dynamically configured dependencies.
|
||||
* The allow/denyList options can be used to limit what modules the runtime
|
||||
* will install/load. It can use '*' as a wildcard that matches anything.
|
||||
*/
|
||||
externalModules: {
|
||||
// autoInstall: false, /** Whether the runtime will attempt to automatically install missing modules */
|
||||
// autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */
|
||||
// palette: { /** Configuration for the Palette Manager */
|
||||
// allowInstall: true, /** Enable the Palette Manager in the editor */
|
||||
// allowUpdate: true, /** Allow modules to be updated in the Palette Manager */
|
||||
// allowUpload: true, /** Allow module tgz files to be uploaded and installed */
|
||||
// allowList: ['*'],
|
||||
// denyList: [],
|
||||
// allowUpdateList: ['*'],
|
||||
// denyUpdateList: []
|
||||
// },
|
||||
// modules: { /** Configuration for node-specified modules */
|
||||
// allowInstall: true,
|
||||
// allowList: [],
|
||||
// denyList: []
|
||||
// }
|
||||
},
|
||||
/** Configure how the runtime will handle external npm modules.
|
||||
* This covers:
|
||||
* - whether the editor will allow new node modules to be installed
|
||||
* - whether nodes, such as the Function node are allowed to have their
|
||||
* own dynamically configured dependencies.
|
||||
* The allow/denyList options can be used to limit what modules the runtime
|
||||
* will install/load. It can use '*' as a wildcard that matches anything.
|
||||
*/
|
||||
externalModules: {
|
||||
// autoInstall: false, /** Whether the runtime will attempt to automatically install missing modules */
|
||||
// autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */
|
||||
// palette: { /** Configuration for the Palette Manager */
|
||||
// allowInstall: true, /** Enable the Palette Manager in the editor */
|
||||
// allowUpdate: true, /** Allow modules to be updated in the Palette Manager */
|
||||
// allowUpload: true, /** Allow module tgz files to be uploaded and installed */
|
||||
// allowList: ['*'],
|
||||
// denyList: [],
|
||||
// allowUpdateList: ['*'],
|
||||
// denyUpdateList: []
|
||||
// },
|
||||
// modules: { /** Configuration for node-specified modules */
|
||||
// allowInstall: true,
|
||||
// allowList: [],
|
||||
// denyList: []
|
||||
// }
|
||||
},
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
@@ -4,7 +4,7 @@ const path = require("path");
|
||||
const fs = require("fs-extra");
|
||||
const should = require("should");
|
||||
|
||||
const LATEST = "2";
|
||||
const LATEST = "3";
|
||||
|
||||
function generateScript() {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@@ -906,6 +906,7 @@ describe('inject node', function() {
|
||||
msg.should.have.property("str1", "1"); //injected prop
|
||||
msg.should.have.property("num1", 1); //injected prop
|
||||
msg.should.have.property("bool1", true); //injected prop
|
||||
msg.should.have.property("jsonata1", "AB"); //injected prop
|
||||
|
||||
helper.clearFlows().then(function() {
|
||||
done();
|
||||
@@ -919,6 +920,7 @@ describe('inject node', function() {
|
||||
{p:"str1", v:"1", vt:"str"}, //new prop
|
||||
{p:"num1", v:"1", vt:"num"}, //new prop
|
||||
{p:"bool1", v:"true", vt:"bool"}, //new prop
|
||||
{p:"jsonata1", v:'"A" & "B"', vt:"jsonata"}, //new prop
|
||||
]})
|
||||
.expect(200).end(function(err) {
|
||||
if (err) {
|
||||
|
@@ -693,19 +693,19 @@ describe('CSV node', function() {
|
||||
describe('json object to csv', function() {
|
||||
|
||||
it('should convert a simple object back to a csv', function(done) {
|
||||
var flow = [ { id:"n1", type:"csv", temp:"a,b,c,,e", wires:[["n2"]] },
|
||||
var flow = [ { id:"n1", type:"csv", temp:"a,b,c,,e,f", wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"} ];
|
||||
helper.load(csvNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('payload', '4,foo,true,,0\n');
|
||||
msg.should.have.property('payload', '4,foo,true,,0,"Hello\nWorld"\n');
|
||||
done();
|
||||
}
|
||||
catch(e) { done(e); }
|
||||
});
|
||||
var testJson = { e:0, d:1, b:"foo", c:true, a:4 };
|
||||
var testJson = { e:0, d:1, b:"foo", c:true, a:4, f:"Hello\nWorld" };
|
||||
n1.emit("input", {payload:testJson});
|
||||
});
|
||||
});
|
||||
@@ -777,7 +777,7 @@ describe('CSV node', function() {
|
||||
}
|
||||
catch(e) { done(e); }
|
||||
});
|
||||
var testJson = [{ d: 1, b: 3, c: 2, a: 4 },{d:4,a:1,c:3,b:2}];
|
||||
var testJson = [{ d:1, b:3, c:2, a:4 },{d:4, a:1, c:3, b:2}];
|
||||
n1.emit("input", {payload:testJson});
|
||||
});
|
||||
});
|
||||
@@ -790,12 +790,12 @@ describe('CSV node', function() {
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('payload', 'a,b,c,d\n4,3,2,1\n1,2,3,4\n');
|
||||
msg.should.have.property('payload', 'a,b,c,d\n4,3,2,1\n1,2,3,"a\nb"\n');
|
||||
done();
|
||||
}
|
||||
catch(e) { done(e); }
|
||||
});
|
||||
var testJson = [{ d: 1, b: 3, c: 2, a: 4 },{d:4,a:1,c:3,b:2}];
|
||||
var testJson = [{ d:1, b:3, c:2, a:4 },{d:"a\nb", a:1, c:3, b:2}];
|
||||
n1.emit("input", {payload:testJson});
|
||||
});
|
||||
});
|
||||
@@ -826,12 +826,12 @@ describe('CSV node', function() {
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('payload', 'd,b,c,a\n1,3,2,4\n4,2,3,1\n');
|
||||
msg.should.have.property('payload', 'd,b,c,a\n1,3,2,4\n4,"f\ng",3,1\n');
|
||||
done();
|
||||
}
|
||||
catch(e) { done(e); }
|
||||
});
|
||||
var testJson = [{ d: 1, b: 3, c: 2, a: 4 },{d:4,a:1,c:3,b:2}];
|
||||
var testJson = [{ d: 1, b: 3, c: 2, a: 4 },{d:4,a:1,c:3,b:"f\ng"}];
|
||||
n1.emit("input", {payload:testJson});
|
||||
});
|
||||
});
|
||||
@@ -844,12 +844,12 @@ describe('CSV node', function() {
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('payload', ',0,1,foo,"ba""r","di,ng"\n');
|
||||
msg.should.have.property('payload', ',0,1,foo,"ba""r","di,ng","fa\nba"\n');
|
||||
done();
|
||||
}
|
||||
catch(e) { done(e); }
|
||||
});
|
||||
var testJson = ["",0,1,"foo",'ba"r','di,ng'];
|
||||
var testJson = ["",0,1,"foo",'ba"r','di,ng',"fa\nba"];
|
||||
n1.emit("input", {payload:testJson});
|
||||
});
|
||||
});
|
||||
@@ -898,12 +898,12 @@ describe('CSV node', function() {
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('payload', 'col1,col2,col3,col4\r\nH1,H2,H3,H4\r\nA,B,,\r\nA,,C,\r\nA,,,D\r\n');
|
||||
msg.should.have.property('payload', 'col1,col2,col3,col4\r\nH1,H2,H3,H4\r\nA,B,,\r\nA,,C,\r\nA,,,"D\nE"\r\n');
|
||||
done();
|
||||
}
|
||||
catch(e) { done(e); }
|
||||
});
|
||||
var testJson = [{"col1":"H1","col2":"H2","col3":"H3","col4":"H4"},{"col1":"A","col2":"B"},{"col1":"A","col3":"C"},{"col1":"A","col4":"D"}];
|
||||
var testJson = [{"col1":"H1","col2":"H2","col3":"H3","col4":"H4"},{"col1":"A","col2":"B"},{"col1":"A","col3":"C"},{"col1":"A","col4":"D\nE"}];
|
||||
n1.emit("input", {payload:testJson});
|
||||
});
|
||||
});
|
||||
|
@@ -388,6 +388,19 @@ describe("@node-red/util/util", function() {
|
||||
result[0].should.eql(1);
|
||||
result[1].should.eql(2);
|
||||
});
|
||||
it('throws an error if buffer data is not array or string', function (done) {
|
||||
try {
|
||||
var result = util.evaluateNodeProperty('12','bin');
|
||||
done("should throw an error");
|
||||
} catch (err) {
|
||||
if (err.code === "INVALID_BUFFER_DATA") {
|
||||
done();
|
||||
}
|
||||
else {
|
||||
done("should throw an error");
|
||||
}
|
||||
}
|
||||
});
|
||||
it('returns msg property',function() {
|
||||
var result = util.evaluateNodeProperty('foo.bar','msg',{},{foo:{bar:"123"}});
|
||||
result.should.eql("123");
|
||||
|
Reference in New Issue
Block a user