Compare commits

...

77 Commits
3.1.0 ... v2.x

Author SHA1 Message Date
Nick O'Leary
f91762ec8f Merge pull request #3832 from node-red/update-gen-pub-script
Update gen pub script
2022-08-04 14:09:28 +01:00
Nick O'Leary
7f698610df Update generate-publish-script with latest release number 2022-08-04 14:08:04 +01:00
Nick O'Leary
bee06f64f5 Merge pull request #3831 from node-red/release-223
Update changelog
2022-08-04 14:01:45 +01:00
Nick O'Leary
53af4231af Update changelog 2022-08-04 14:00:37 +01:00
Nick O'Leary
2ea4b15dca Merge pull request #3829 from Steve-Mcl/backport-3776
Allow HTTP Headers not in spec (backport to v2.x)
2022-08-04 13:49:43 +01:00
Nick O'Leary
ce09f56dcc Update packages/node_modules/@node-red/nodes/locales/en-US/messages.json 2022-08-04 13:49:04 +01:00
Ben Hardill
6c648e4bab Allow HTTP Headers not in spec
potential fix for #3772
2022-08-04 13:48:03 +01:00
Nick O'Leary
72661aa159 Merge pull request #3828 from Steve-Mcl/backport-3762
Fix storing subflow credential type when input has multiple types (backport to v2.x)
2022-08-04 13:00:33 +01:00
Nick O'Leary
16e79d6bd2 Merge pull request #3827 from Steve-Mcl/backport-3754
Fix change node, not handling from field properly when using context (backport to v2.x)
2022-08-04 13:00:16 +01:00
Nick O'Leary
726fac97d2 Merge pull request #3826 from Steve-Mcl/backport-3728
Do not remove unknown credentials of Subflow Modules (backport to v2.x)
2022-08-04 13:00:06 +01:00
Nick O'Leary
efc3882961 Merge pull request #3825 from Steve-Mcl/backport-3716
Fix CSV node to handle \n when outputting text fields (backport to v2.x)
2022-08-04 12:59:15 +01:00
Nick O'Leary
489c456271 Merge pull request #3824 from Steve-Mcl/backport-3709
Fix delay rate limit last timing when empty (backport to v2.x)
2022-08-04 12:59:06 +01:00
Nick O'Leary
970be7b5f2 Merge pull request #3823 from Steve-Mcl/backport-3715
Ensure 'hidden flow' count doesn't include subflows (backport to v2.x)
2022-08-04 12:58:49 +01:00
Nick O'Leary
b94fdd2f79 Merge pull request #3822 from Steve-Mcl/backport-3703
Fix credential type input item of subflow template (backport to v2.x)
2022-08-04 12:58:39 +01:00
Nick O'Leary
66f6bc59fa Merge pull request #3821 from Steve-Mcl/backport-3670
Join-reduce keep existing msg properties (backport to v2.x)
2022-08-04 12:58:21 +01:00
Stephen McLaughlin
455f2dc800 delete console.log artefact 2022-08-04 12:35:34 +01:00
Nick O'Leary
aed2b8e46d Fix storing subflow credential type when input has multiple types
Fixes #3749
2022-08-04 10:39:04 +01:00
Franck Mourre
633afbbb44 Fix change node, not handling from field properly when using context 2022-08-04 10:00:09 +01:00
Nick O'Leary
6c797e35cf Update packages/node_modules/@node-red/runtime/lib/nodes/credentials.js 2022-08-04 09:57:14 +01:00
Nick O'Leary
30b8a95421 Do not remove unknown credentials of Subflow Modules
Fixes #3641
2022-08-04 09:57:14 +01:00
Dave Conway-Jones
7665971b52 Fix CSV node to handle \n when outputting text fields
and add tests
2022-08-04 09:56:26 +01:00
Dave Conway-Jones
ba6dc3117f Delay node - add example for simple queue with release 2022-08-04 09:55:04 +01:00
Dave Conway-Jones
73fcc05a54 Fix delay rate limit last timing when empty 2022-08-04 09:55:04 +01:00
Nick O'Leary
fd409b0636 Ensure 'hidden flow' count doesn't include subflows
Fixes #3707
2022-08-04 09:53:04 +01:00
Hiroyasu Nishiyama
29287149b4 remove line break to prevent jshint error 2022-08-04 09:43:45 +01:00
Hiroyasu Nishiyama
18c0ec990a fix type selector of subflow environment variable 2022-08-04 09:43:45 +01:00
Hiroyasu Nishiyama
ee2b151b7a fix credential type input item of subflow template 2022-08-04 09:43:44 +01:00
Steve-Mcl
2d7a068644 Join-reduce keep existing msg properties
see https://discourse.nodered.org/t/no-response-object-after-split-join/63919
2022-08-04 09:39:43 +01:00
Nick O'Leary
5c5855751c Merge pull request #3735 from kazuhitoyokoi/v2.x-backports
Backports to v2.x
2022-07-04 21:03:03 +01:00
Kazuhito Yokoi
396b3b64ff Fix typo in message catalog (Backport node-red#3724 to v2.x) 2022-07-04 21:38:25 +09:00
Kazuhito Yokoi
48e33a472b Fix use default button for node icon (Backport node-red#3714 to v2.x) 2022-07-04 21:35:44 +09:00
Nick O'Leary
a7375e4e8f Merge pull request #3705 from kazuhitoyokoi/v2.x-addjpn
Backports to v2.x
2022-06-27 14:15:26 +01:00
Kazuhito Yokoi
42732db2b0 Fix typos in message catalog (Backport node-red#3622 to v2.x) 2022-06-26 11:20:05 +09:00
Kazuhito Yokoi
7c830856df Remove unnecessary spaces (Backport node-red#3688 to v2.x) 2022-06-26 11:16:01 +09:00
Kazuhito Yokoi
5dad33dc77 Change indents from tabs to 4 spaces (Backport node-red#3512 to v2.x) 2022-06-26 11:06:12 +09:00
Kazuhito Yokoi
cbdb988d02 Fix indents in other code except message catalog (Backport node-red#3622 to v2.x) 2022-06-26 10:57:17 +09:00
Kazuhito Yokoi
e944dfecc2 Add Japanese translations (Backport node-red#3688 to v2.x) 2022-06-26 10:08:22 +09:00
Kazuhito Yokoi
b2c2b9bd14 Add Japanese translations (Backport node-red#3576 to v2.x) 2022-06-26 09:48:42 +09:00
Nick O'Leary
0ed7f6ce4e Merge pull request #3665 from Steve-Mcl/backport-3647
Fix to extend escaped subflow category characters (backport #3647 to v2.x)
2022-06-16 11:16:54 +01:00
Nick O'Leary
74e3b9e9ed Merge pull request #3664 from Steve-Mcl/backport-3643
Reset mouse state when switching tabs (Backport #3643 to v2.x)
2022-06-16 11:16:34 +01:00
Nick O'Leary
7fd884c68f Merge pull request #3663 from Steve-Mcl/backport-3646
Fix to sanitize tab name (Backport #3646 to v2.x)
2022-06-16 11:16:18 +01:00
Nick O'Leary
3f29283724 Merge pull request #3662 from Steve-Mcl/backport-3645
Fix ESM module loading in Function node (Backport #3645 to v2.x)
2022-06-16 11:15:55 +01:00
Nick O'Leary
ea303495b0 Merge pull request #3661 from Steve-Mcl/backport-3633
fix new folder menu of save to library dialog (backport #3633 to v2.x)
2022-06-16 11:14:43 +01:00
Nick O'Leary
9d39709038 Merge pull request #3660 from Steve-Mcl/backport-3632
Fix JSONata evaluation of inject button (Backport 3632 to v2.x)
2022-06-16 11:14:26 +01:00
Nick O'Leary
facb6143c3 Merge pull request #3659 from Steve-Mcl/backport-3624
fix buffer parse error message of evaluateNodeProperty (backports #3624 for v2.x)
2022-06-16 11:14:13 +01:00
Nick O'Leary
68a2786ff2 Merge pull request #3658 from Steve-Mcl/backport-3630
Dont delete TCP socket twice (backport Backport: #3630 to v2.x)
2022-06-16 11:13:17 +01:00
Nick O'Leary
b2b31670f8 Merge pull request #3657 from Steve-Mcl/backport-3611
Ensure ACE worker options are set (Backport 3611 to v2.x)
2022-06-16 11:13:02 +01:00
Nick O'Leary
355fb724f8 Merge pull request #3656 from Steve-Mcl/backport-3548
Fix browser console error Uncaught TypeError when searching certain terms (Backport #3548 to v2.x)
2022-06-16 11:12:47 +01:00
Nick O'Leary
d925f12aea Merge pull request #3655 from Steve-Mcl/dup-hist-event
Remove duplicate history add of ungroup event (backport #3581to v2.x)
2022-06-16 11:12:11 +01:00
Nick O'Leary
428ed090f3 Merge pull request #3654 from Steve-Mcl/mqtt-backport
Mqtt fixes in v3 for v2.x (backports #3563 #3594 #3626 to v2.x)
2022-06-16 11:11:50 +01:00
Steve-Mcl
9946132658 extend escaped subflow category characters
@HiroyasuNishiyama commit 30d88bbe7e

Co-authored-by: HiroyasuNishiyama <hiroyasu.nishiyama.uq@hitachi.com>
2022-06-14 07:47:54 +01:00
Steve-Mcl
30f08a9f17 Track mouse release outside workspace so current action completes
manual cherry pick a16032a
2022-06-14 07:39:19 +01:00
Nick O'Leary
83f1729d83 Reset mouse state when switching tabs
Fixes #3639
2022-06-14 07:35:11 +01:00
Hiroyasu Nishiyama
3667de18a9 sanitize tab name 2022-06-13 14:56:57 +01:00
Nick O'Leary
bda8e86ea4 Fix ESM module loading in Function node
Fixes #3627
2022-06-13 14:53:06 +01:00
Hiroyasu Nishiyama
7920fa1dd4 fix new folder menu of save to library dialog 2022-06-13 14:48:16 +01:00
Hiroyasu Nishiyama
cb06690553 update test for inject node 2022-06-13 13:52:28 +01:00
Hiroyasu Nishiyama
4b4e5a0861 fix JSONata evaluation of inject button 2022-06-13 13:52:28 +01:00
Hiroyasu Nishiyama
e5952f082e fix buffer parse error message of evaluateNodeProperty 2022-06-13 13:48:22 +01:00
Stephen McLaughlin
c7994b6393 Dont delete TCP socket twice 2022-06-13 13:40:50 +01:00
Steve-Mcl
39ac6b0d46 fix typo 2022-06-13 13:33:30 +01:00
Stephen McLaughlin
e22bf22b61 Ensure ACE worker options are set
fixes #3610
2022-06-13 13:25:43 +01:00
Steve-Mcl
92dd80ac49 declare undeclared loop var 2022-06-13 13:21:46 +01:00
Steve-Mcl
9442e2a993 Prevent error when uses search term is used
fixes #3578
2022-06-13 13:21:46 +01:00
Nick O'Leary
6df7f50f9f Remove duplicate history add of ungroup event
Fixes #3581
2022-06-13 13:16:57 +01:00
Steve-Mcl
933cad888f add changes from #3563
Add client and Runtime MQTT topic validation and fix subsequent connection lockup
(that arises due to bad birth/will topic)
backport-2.x
2022-06-13 12:57:22 +01:00
Stephen McLaughlin
8133c5e834 define noproxy variable 2022-06-13 12:16:10 +01:00
Stephen McLaughlin
e7f6549cb6 Remove unnecessary call to clientRemoveListeners
Also, merge the non JSDOC comment into the JSDOC comment
2022-06-13 12:15:27 +01:00
Phil Day
2ded07c765 call client.end with force=true on timeout 2022-06-13 12:15:27 +01:00
Phil Day
6a799e7e2a Update packages/node_modules/@node-red/nodes/core/network/10-mqtt.js
Co-authored-by: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com>
2022-06-13 12:15:27 +01:00
Phil Day
1d40378f8c Update packages/node_modules/@node-red/nodes/core/network/10-mqtt.js
Co-authored-by: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com>
2022-06-13 12:15:27 +01:00
Phil Day
bcab1cf096 Update packages/node_modules/@node-red/nodes/core/network/10-mqtt.js
Co-authored-by: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com>
2022-06-13 12:15:27 +01:00
Phil Day
03484fd5cd Updated to cover the removal of individual event handlers 2022-06-13 12:15:27 +01:00
Phil Day
1eea371124 Revent change of grunt version 2022-06-13 12:15:27 +01:00
Phil Day
02d9ad479d Track which event handlers we add to the mqtt client so we can removed them cleanly 2022-06-13 12:15:23 +01:00
Phil Day
3be75fa822 Add Force parameter mqtt client.end() when called in disconnect 2022-06-13 12:14:38 +01:00
Stephen McLaughlin
ca204dea2d Use new validation option to return better label
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-06-13 12:11:42 +01:00
50 changed files with 735 additions and 318 deletions

View File

@@ -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

View File

@@ -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",

View File

@@ -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",

View File

@@ -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": "バージョンコントロールタブを表示"
}
}

View File

@@ -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}} を押すとプロパティ設定画面が表示されます。"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-client",
"version": "2.2.2",
"version": "2.2.3",
"license": "Apache-2.0",
"repository": {
"type": "git",

View File

@@ -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];
}
}
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);
}
});

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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)
}

View File

@@ -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;

View File

@@ -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"
})
}

View File

@@ -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 {

View File

@@ -254,7 +254,7 @@ button.red-ui-palette-editor-upload-button {
padding: 2px 8px;
}
form {
width: 0;
width: 0;
}
}
.red-ui-palette-editor-upload {

View File

@@ -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) {

View File

@@ -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());
}

View File

@@ -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) {

View File

@@ -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();
}

View File

@@ -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: {

View File

@@ -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();
}

View File

@@ -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();

View File

@@ -214,6 +214,10 @@ in your Node-RED user directory (${RED.settings.userDir}).
delete options.headers[h];
}
})
if (node.insecureHTTPParser) {
options.insecureHTTPParser = true
}
}
],
beforeRedirect: [

View File

@@ -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}));
});

View File

@@ -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

View File

@@ -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 {

View File

@@ -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">

View 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": []
}
]

View File

@@ -525,7 +525,8 @@
},
"status": {
"requesting": "requesting"
}
},
"insecureHTTPParser": "Disable strict HTTP parsing"
},
"websocket": {
"label": {

View File

@@ -28,7 +28,7 @@
<p>返却/sendの対象は次のとおりです:</p>
<ul>
<li>単一メッセージオブジェクト - 最初の出力に接続されたノードに渡されます</li>
<li>メッセージオブジェクトの配列 - 対応する出力に接続されたノードに渡されます</li>
<li>メッセージオブジェクトの配列 - 対応する出力に接続されたノードに渡されます</li>
</ul>
<p>: 初期化処理の実行はノードの初期化中に行われますそのため初期化処理タブにsendを記述した場合に後続ードでメッセージを受け取れないことがあります</p>
<p>配列要素が配列の場合には複数のメッセージを対応する出力に送出します</p>

View File

@@ -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>

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/nodes",
"version": "2.2.2",
"version": "2.2.3",
"license": "Apache-2.0",
"repository": {
"type": "git",

View File

@@ -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) {

View File

@@ -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",

View File

@@ -390,7 +390,6 @@ class Subflow extends Flow {
}
name = newName;
}
var parent = this.parent;
if (parent) {

View File

@@ -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;
}
}
}
}

View File

@@ -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": "セーフモードでフローを停止しました。開始するためにはデプロイしてください",

View File

@@ -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",

View File

@@ -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);

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/util",
"version": "2.2.2",
"version": "2.2.3",
"license": "Apache-2.0",
"repository": {
"type": "git",

View File

@@ -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",

View File

@@ -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: []
// }
},
/*******************************************************************************

View File

@@ -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) => {

View File

@@ -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) {

View File

@@ -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});
});
});

View File

@@ -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");