Compare commits

..

21 Commits

Author SHA1 Message Date
Nick O'Leary
1a3cc06935 Use rfdc module for cloning when we know its pure JSON 2024-05-03 16:45:50 +01:00
Nick O'Leary
a712a9363b Merge pull request #4674 from kazuhitoyokoi/dev-addjpn
Add Japanese translations for 4.0.0-beta.2
2024-05-03 16:18:50 +01:00
Kazuhito Yokoi
437c28e2b8 Fix typos in welcome tour for 4.0.0-beta.2 2024-04-28 21:27:25 +09:00
Kazuhito Yokoi
c05d18ada1 Add Japanese translations for 4.0.0-beta.2 2024-04-28 21:22:15 +09:00
Nick O'Leary
cfb300ec06 Merge pull request #4672 from node-red/allow-blank-env-var-substitutions
Allow blank strings to be used for env var property substitutions
2024-04-24 23:37:23 +02:00
Nick O'Leary
236e668201 Allow blank strings to be used for env var property substitutions
Fixes #4663
2024-04-24 22:58:11 +02:00
Nick O'Leary
c9b902c2b4 Merge pull request #4658 from node-red/fix-subflow-conf-type
Fix saving of conf-type properties in module packaged subflows
2024-04-23 23:45:29 +02:00
Nick O'Leary
ac8b1e19b7 Merge pull request #4662 from hardillb/timeout-npm-install
Add npm install timeout notification
2024-04-23 23:44:51 +02:00
Ben Hardill
148e64c3da Update packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2024-04-18 14:22:50 +01:00
Ben Hardill
c6289ebb2c Merge branch 'dev' into timeout-npm-install 2024-04-18 12:12:53 +01:00
Ben Hardill
5f4ece6813 Move translation 2024-04-18 11:47:49 +01:00
Ben Hardill
c990ec39d6 revert DELETE change 2024-04-18 11:35:51 +01:00
Ben Hardill
1fdc600ecd Add npm install timeout notification
part of https://github.com/node-red/node-red/issues/4622
2024-04-18 11:27:32 +01:00
Nick O'Leary
e354d2ce29 Fix saving of conf-type properties in module packaged subflows 2024-04-12 14:08:07 +01:00
Nick O'Leary
d218af8619 Merge branch 'master' into dev 2024-04-12 13:04:54 +01:00
Nick O'Leary
29ed5b2792 Merge pull request #4655 from node-red/rel319
Bump for 3.1.9 release
2024-04-11 19:22:24 +01:00
Nick O'Leary
e39216e65a Bump for 3.1.9 release 2024-04-11 19:15:46 +01:00
Nick O'Leary
7ac7f9b4c8 Merge pull request #4654 from node-red/fix-subflow-recursion-check
Prevent subflow being added to itself
2024-04-11 19:12:43 +01:00
Stephen McLaughlin
4709eb9d49 Merge pull request #4652 from node-red/fix-windows-spawn
Fix use of spawn on windows with cmd files
2024-04-11 17:51:13 +01:00
Nick O'Leary
c13b8266dd Prevent subflow being added to itself 2024-04-11 17:05:10 +01:00
Nick O'Leary
bd58431603 Fix use of spawn on windows with cmd files 2024-04-11 14:40:29 +01:00
19 changed files with 243 additions and 169 deletions

View File

@@ -50,6 +50,13 @@ Nodes
- Let debug node status msg length be settable via settings (#4402) @dceejay
- Feat: Add ability to set headers for WebSocket client (#4436) @marcus-j-davies
#### 3.1.9: Maintenance Release
- Prevent subflow being added to itself (#4654) @knolleary
- Fix use of spawn on windows with cmd files (#4652) @knolleary
- Guard refresh of unknown subflow (#4640) @knolleary
- Fix subflow module sending messages to debug sidebar (#4642) @knolleary
#### 3.1.8: Maintenance Release
- Add validation and error handling on subflow instance properties (#4632) @knolleary

View File

@@ -73,8 +73,9 @@
"passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2",
"raw-body": "2.5.2",
"rfdc": "^1.3.1",
"semver": "7.5.4",
"tar": "6.1.13",
"tar": "6.2.1",
"tough-cookie": "4.1.3",
"uglify-js": "3.17.4",
"uuid": "9.0.0",

View File

@@ -643,6 +643,7 @@
"errors": {
"catalogLoadFailed": "<p>Failed to load node catalogue.</p><p>Check the browser console for more information</p>",
"installFailed": "<p>Failed to install: __module__</p><p>__message__</p><p>Check the log for more information</p>",
"installTimeout": "<p>Install continuing the background.</p><p>Nodes will appear in palette when complete. Check the log for more information.</p>",
"removeFailed": "<p>Failed to remove: __module__</p><p>__message__</p><p>Check the log for more information</p>",
"updateFailed": "<p>Failed to update: __module__</p><p>__message__</p><p>Check the log for more information</p>",
"enableFailed": "<p>Failed to enable: __module__</p><p>__message__</p><p>Check the log for more information</p>",
@@ -657,6 +658,9 @@
"body": "<p>Removing '__module__'</p><p>Removing the node will uninstall it from Node-RED. The node may continue to use resources until Node-RED is restarted.</p>",
"title": "Remove nodes"
},
"removePlugin": {
"body": "<p>Removed plugin __module__. Please reload the editor to clear left-overs.</p>"
},
"update": {
"body": "<p>Updating '__module__'</p><p>Updating the node will require a restart of Node-RED to complete the update. This must be done manually.</p>",
"title": "Update nodes"
@@ -668,7 +672,8 @@
"review": "Open node information",
"install": "Install",
"remove": "Remove",
"update": "Update"
"update": "Update",
"understood": "Understood"
}
}
}

View File

@@ -614,6 +614,8 @@
},
"nodeCount": "__label__ 個のノード",
"nodeCount_plural": "__label__ 個のノード",
"pluginCount": "__count__ 個のプラグイン",
"pluginCount_plural": "__count__ 個のプラグイン",
"moduleCount": "__count__ 個のモジュール",
"moduleCount_plural": "__count__ 個のモジュール",
"inuse": "使用中",
@@ -641,6 +643,7 @@
"errors": {
"catalogLoadFailed": "<p>ノードのカタログの読み込みに失敗しました。</p><p>詳細はブラウザのコンソールを確認してください。</p>",
"installFailed": "<p>追加処理が失敗しました: __module__</p><p>__message__</p><p>詳細はログを確認してください。</p>",
"installTimeout": "<p>バックグラウンドでインストールが継続されます。</p><p>完了した時にノードが表示されます。詳細はログを確認してください。</p>",
"removeFailed": "<p>削除処理が失敗しました: __module__</p><p>__message__</p><p>詳細はログを確認してください。</p>",
"updateFailed": "<p>更新処理が失敗しました: __module__</p><p>__message__</p><p>詳細はログを確認してください。</p>",
"enableFailed": "<p>有効化処理が失敗しました: __module__</p><p>__message__</p><p>詳細はログを確認してください。</p>",
@@ -655,6 +658,9 @@
"body": "<p>__module__ を削除します。</p><p>Node-REDからードを削除します。ードはNode-REDが再起動されるまで、リソースを使い続ける可能性があります。</p>",
"title": "ノードを削除"
},
"removePlugin": {
"body": "<p>プラグイン __module__ を削除しました。ブラウザを再読み込みして残った表示を消してください。</p>"
},
"update": {
"body": "<p>__module__ を更新します。</p><p>更新を完了するには手動でNode-REDを再起動する必要があります。</p>",
"title": "ノードの更新"
@@ -666,7 +672,8 @@
"review": "ノードの情報を参照",
"install": "追加",
"remove": "削除",
"update": "更新"
"update": "更新",
"understood": "了解"
}
}
}

View File

@@ -133,7 +133,7 @@ RED.palette.editor = (function() {
}).done(function(data,textStatus,xhr) {
callback();
}).fail(function(xhr,textStatus,err) {
callback(xhr);
callback(xhr,textStatus,err);
});
}
function removeNodeModule(id,callback) {
@@ -1346,13 +1346,13 @@ RED.palette.editor = (function() {
});
if (!found_onremove) {
let removeNotify = RED.notify("Removed plugin " + entry.name + ". Please reload the editor to clear left-overs.",{
let removeNotify = RED.notify(RED._("palette.editor.confirm.removePlugin.body",{module:entry.name}),{
modal: true,
fixed: true,
type: 'warning',
buttons: [
{
text: "Understood",
text: RED._("palette.editor.confirm.button.understood"),
class:"primary",
click: function(e) {
removeNotify.close();
@@ -1405,9 +1405,28 @@ RED.palette.editor = (function() {
RED.actions.invoke("core:show-event-log");
});
RED.eventLog.startEvent(RED._("palette.editor.confirm.button.install")+" : "+entry.id+" "+entry.version);
installNodeModule(entry.id,entry.version,entry.pkg_url,function(xhr) {
installNodeModule(entry.id,entry.version,entry.pkg_url,function(xhr, textStatus,err) {
spinner.remove();
if (xhr) {
if (err && xhr.status === 504) {
var notification = RED.notify(RED._("palette.editor.errors.installTimeout"), {
modal: true,
fixed: true,
buttons: [
{
text: RED._("common.label.close"),
click: function() {
notification.close();
}
},{
text: RED._("eventLog.view"),
click: function() {
notification.close();
RED.actions.invoke("core:show-event-log");
}
}
]
})
} else if (xhr) {
if (xhr.responseJSON) {
var notification = RED.notify(RED._('palette.editor.errors.installFailed',{module: entry.id,message:xhr.responseJSON.message}),{
type: 'error',

View File

@@ -1363,7 +1363,7 @@ RED.subflow = (function() {
break;
case "conf-types":
item.value = input.val()
item.type = data.parent.value;
item.type = "conf-type"
}
if (ui.type === "cred" || item.type !== data.parent.type || item.value !== data.parent.value) {
env.push(item);

View File

@@ -646,120 +646,128 @@ RED.view = (function() {
}
d3.event = event;
var selected_tool = $(ui.draggable[0]).attr("data-palette-type");
var result = createNode(selected_tool);
if (!result) {
return;
}
var historyEvent = result.historyEvent;
var nn = RED.nodes.add(result.node);
var showLabel = RED.utils.getMessageProperty(RED.settings.get('editor'),"view.view-node-show-label");
if (showLabel !== undefined && (nn._def.hasOwnProperty("showLabel")?nn._def.showLabel:true) && !nn._def.defaults.hasOwnProperty("l")) {
nn.l = showLabel;
}
var helperOffset = d3.touches(ui.helper.get(0))[0]||d3.mouse(ui.helper.get(0));
var helperWidth = ui.helper.width();
var helperHeight = ui.helper.height();
var mousePos = d3.touches(this)[0]||d3.mouse(this);
try {
var isLink = (nn.type === "link in" || nn.type === "link out")
var hideLabel = nn.hasOwnProperty('l')?!nn.l : isLink;
var label = RED.utils.getNodeLabel(nn, nn.type);
var labelParts = getLabelParts(label, "red-ui-flow-node-label");
if (hideLabel) {
nn.w = node_height;
nn.h = Math.max(node_height,(nn.outputs || 0) * 15);
} else {
nn.w = Math.max(node_width,20*(Math.ceil((labelParts.width+50+(nn._def.inputs>0?7:0))/20)) );
nn.h = Math.max(6+24*labelParts.lines.length,(nn.outputs || 0) * 15, 30);
var result = createNode(selected_tool);
if (!result) {
return;
}
} catch(err) {
}
var historyEvent = result.historyEvent;
var nn = RED.nodes.add(result.node);
mousePos[1] += this.scrollTop + ((helperHeight/2)-helperOffset[1]);
mousePos[0] += this.scrollLeft + ((helperWidth/2)-helperOffset[0]);
mousePos[1] /= scaleFactor;
mousePos[0] /= scaleFactor;
var showLabel = RED.utils.getMessageProperty(RED.settings.get('editor'),"view.view-node-show-label");
if (showLabel !== undefined && (nn._def.hasOwnProperty("showLabel")?nn._def.showLabel:true) && !nn._def.defaults.hasOwnProperty("l")) {
nn.l = showLabel;
}
nn.x = mousePos[0];
nn.y = mousePos[1];
var helperOffset = d3.touches(ui.helper.get(0))[0]||d3.mouse(ui.helper.get(0));
var helperWidth = ui.helper.width();
var helperHeight = ui.helper.height();
var mousePos = d3.touches(this)[0]||d3.mouse(this);
var minX = nn.w/2 -5;
if (nn.x < minX) {
nn.x = minX;
}
var minY = nn.h/2 -5;
if (nn.y < minY) {
nn.y = minY;
}
var maxX = space_width -nn.w/2 +5;
if (nn.x > maxX) {
nn.x = maxX;
}
var maxY = space_height -nn.h +5;
if (nn.y > maxY) {
nn.y = maxY;
}
try {
var isLink = (nn.type === "link in" || nn.type === "link out")
var hideLabel = nn.hasOwnProperty('l')?!nn.l : isLink;
if (snapGrid) {
var gridOffset = RED.view.tools.calculateGridSnapOffsets(nn);
nn.x -= gridOffset.x;
nn.y -= gridOffset.y;
}
var label = RED.utils.getNodeLabel(nn, nn.type);
var labelParts = getLabelParts(label, "red-ui-flow-node-label");
if (hideLabel) {
nn.w = node_height;
nn.h = Math.max(node_height,(nn.outputs || 0) * 15);
} else {
nn.w = Math.max(node_width,20*(Math.ceil((labelParts.width+50+(nn._def.inputs>0?7:0))/20)) );
nn.h = Math.max(6+24*labelParts.lines.length,(nn.outputs || 0) * 15, 30);
}
} catch(err) {
}
var linkToSplice = $(ui.helper).data("splice");
if (linkToSplice) {
spliceLink(linkToSplice, nn, historyEvent)
}
mousePos[1] += this.scrollTop + ((helperHeight/2)-helperOffset[1]);
mousePos[0] += this.scrollLeft + ((helperWidth/2)-helperOffset[0]);
mousePos[1] /= scaleFactor;
mousePos[0] /= scaleFactor;
nn.x = mousePos[0];
nn.y = mousePos[1];
var minX = nn.w/2 -5;
if (nn.x < minX) {
nn.x = minX;
}
var minY = nn.h/2 -5;
if (nn.y < minY) {
nn.y = minY;
}
var maxX = space_width -nn.w/2 +5;
if (nn.x > maxX) {
nn.x = maxX;
}
var maxY = space_height -nn.h +5;
if (nn.y > maxY) {
nn.y = maxY;
}
if (snapGrid) {
var gridOffset = RED.view.tools.calculateGridSnapOffsets(nn);
nn.x -= gridOffset.x;
nn.y -= gridOffset.y;
}
var linkToSplice = $(ui.helper).data("splice");
if (linkToSplice) {
spliceLink(linkToSplice, nn, historyEvent)
}
var group = $(ui.helper).data("group");
if (group) {
var oldX = group.x;
var oldY = group.y;
RED.group.addToGroup(group, nn);
var moveEvent = null;
if ((group.x !== oldX) ||
(group.y !== oldY)) {
moveEvent = {
t: "move",
nodes: [{n: group,
ox: oldX, oy: oldY,
dx: group.x -oldX,
dy: group.y -oldY}],
dirty: true
};
}
historyEvent = {
t: 'multi',
events: [historyEvent],
var group = $(ui.helper).data("group");
if (group) {
var oldX = group.x;
var oldY = group.y;
RED.group.addToGroup(group, nn);
var moveEvent = null;
if ((group.x !== oldX) ||
(group.y !== oldY)) {
moveEvent = {
t: "move",
nodes: [{n: group,
ox: oldX, oy: oldY,
dx: group.x -oldX,
dy: group.y -oldY}],
dirty: true
};
if (moveEvent) {
historyEvent.events.push(moveEvent)
}
historyEvent.events.push({
t: "addToGroup",
group: group,
nodes: nn
})
}
historyEvent = {
t: 'multi',
events: [historyEvent],
};
if (moveEvent) {
historyEvent.events.push(moveEvent)
RED.history.push(historyEvent);
RED.editor.validateNode(nn);
RED.nodes.dirty(true);
// auto select dropped node - so info shows (if visible)
clearSelection();
nn.selected = true;
movingSet.add(nn);
updateActiveNodes();
updateSelection();
redraw();
if (nn._def.autoedit) {
RED.editor.edit(nn);
}
} catch (error) {
if (error.code != "NODE_RED") {
RED.notify(RED._("notification.error",{message:error.toString()}),"error");
} else {
RED.notify(RED._("notification.error",{message:error.message}),"error");
}
historyEvent.events.push({
t: "addToGroup",
group: group,
nodes: nn
})
}
RED.history.push(historyEvent);
RED.editor.validateNode(nn);
RED.nodes.dirty(true);
// auto select dropped node - so info shows (if visible)
clearSelection();
nn.selected = true;
movingSet.add(nn);
updateActiveNodes();
updateSelection();
redraw();
if (nn._def.autoedit) {
RED.editor.edit(nn);
}
}
});
@@ -6063,14 +6071,19 @@ RED.view = (function() {
function createNode(type, x, y, z) {
const wasDirty = RED.nodes.dirty()
var m = /^subflow:(.+)$/.exec(type);
var activeSubflow = z ? RED.nodes.subflow(z) : null;
var activeSubflow = (z || RED.workspaces.active()) ? RED.nodes.subflow(z || RED.workspaces.active()) : null;
if (activeSubflow && m) {
var subflowId = m[1];
let err
if (subflowId === activeSubflow.id) {
throw new Error(RED._("notification.error", { message: RED._("notification.errors.cannotAddSubflowToItself") }))
err = new Error(RED._("notification.errors.cannotAddSubflowToItself"))
} else if (RED.nodes.subflowContains(m[1], activeSubflow.id)) {
err = new Error(RED._("notification.errors.cannotAddCircularReference"))
}
if (RED.nodes.subflowContains(m[1], activeSubflow.id)) {
throw new Error(RED._("notification.error", { message: RED._("notification.errors.cannotAddCircularReference") }))
if (err) {
err.code = 'NODE_RED'
throw err
}
}

View File

@@ -16,7 +16,8 @@ export default {
},
{
title: {
"en-US": "Multiplayer Mode"
"en-US": "Multiplayer Mode",
"ja": "複数ユーザ同時利用モード"
},
image: 'images/nr4-multiplayer.png',
description: {
@@ -24,46 +25,58 @@ export default {
to work with when you have multiple people editing flows at the same time.</p>
<p>When this feature is enabled, you will now see who else has the editor open and some
basic information on where they are in the editor.</p>
<p>Check the release post for details on how to enable this feature in your settings file.</p>`
<p>Check the release post for details on how to enable this feature in your settings file.</p>`,
"ja": `<p>本リリースには、複数ユーザが同時にフローを編集する時に、Node-REDをより使いやすくするのための最初の微修正が入っています。</p>
<p>本機能を有効にすると、誰がエディタを開いているか、その人がエディタ上のどこにいるかの基本的な情報が表示されます。</p>
<p>設定ファイルで本機能を有効化する方法の詳細は、リリースの投稿を確認してください。</p>`
}
},
{
title: {
"en-US": "Better Configuration Node UX"
"en-US": "Better Configuration Node UX",
"ja": "設定ードのUXが向上"
},
image: 'images/nr4-config-select.png',
description: {
"en-US": `<p>The Configuration node selection UI has had a small update to have a dedicated 'add' button
next to the select box.</p>
<p>It's a small change, but should make it easier to work with your config nodes.</p>`
<p>It's a small change, but should make it easier to work with your config nodes.</p>`,
"ja": `<p>設定ードを選択するUIが修正され、選択ボックスの隣に専用の「追加」ボタンが追加されました。</p>
<p>微修正ですが設定ノードの操作が容易になります。</p>`
}
},
{
title: {
"en-US": "Remembering palette state"
"en-US": "Remembering palette state",
"ja": "パレットの状態を維持"
},
description: {
"en-US": `<p>The palette now remembers what categories you have hidden between reloads - as well as any
filter you have applied.</p>`
filter you have applied.</p>`,
"ja": `<p>パレット上で非表示にしたカテゴリや適用したフィルタが、リロードしても記憶されるようになりました。</p>`
}
},
{
title: {
"en-US": "Plugins shown in the Palette Manager"
"en-US": "Plugins shown in the Palette Manager",
"ja": "パレット管理にプラグインを表示"
},
image: 'images/nr4-plugins.png',
description: {
"en-US": `<p>The palette manager now shows any plugin modules you have installed, such as
<code>node-red-debugger</code>. Previously they would only be shown if they plugin include
nodes for the palette.</p>`
<code>node-red-debugger</code>. Previously they would only be shown if the plugins include
nodes for the palette.</p>`,
"ja": `<p>パレットの管理に <code>node-red-debugger</code> の様なインストールしたプラグインが表示されます。以前はプラグインにパレット向けのノードが含まれている時のみ表示されていました。</p>`
}
},
{
title: {
"en-US": "That's if for Beta 2!"
"en-US": "That's it for Beta 2!",
"ja": "ベータ2については以上です!"
},
description: {
"en-US": `<p>Keep clicking through to see what was added in Beta 1</p>`
"en-US": `<p>Keep clicking through to see what was added in Beta 1</p>`,
"ja": `<p>クリックを続けてベータ1で追加された内容を確認してください。</p>`
}
},
{

View File

@@ -20,6 +20,7 @@ module.exports = function(RED) {
var exec = require('child_process').exec;
var fs = require('fs');
var isUtf8 = require('is-utf8');
const isWindows = process.platform === 'win32'
function ExecNode(n) {
RED.nodes.createNode(this,n);
@@ -85,9 +86,12 @@ module.exports = function(RED) {
}
});
var cmd = arg.shift();
// Since 18.20.2/20.12.2, it is invalid to call spawn on Windows with a .bat/.cmd file
// without using shell: true.
const opts = isWindows ? { ...node.spawnOpt, shell: true } : node.spawnOpt
/* istanbul ignore else */
node.debug(cmd+" ["+arg+"]");
child = spawn(cmd,arg,node.spawnOpt);
child = spawn(cmd,arg,opts);
node.status({fill:"blue",shape:"dot",text:"pid:"+child.pid});
var unknownCommand = (child.pid === undefined);
if (node.timer !== 0) {

View File

@@ -273,7 +273,7 @@ async function installModule(moduleDetails) {
let extraArgs = triggerPayload.args || [];
let args = ['install', ...extraArgs, installSpec]
log.trace(NPM_COMMAND + JSON.stringify(args));
return exec.run(NPM_COMMAND, args, { cwd: installDir },true)
return exec.run(NPM_COMMAND, args, { cwd: installDir, shell: true },true)
} else {
log.trace("skipping npm install");
}

View File

@@ -25,14 +25,17 @@ const registryUtil = require("./util");
const library = require("./library");
const {exec,log,events,hooks} = require("@node-red/util");
const child_process = require('child_process');
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
let installerEnabled = false;
const plugins = require("./plugins");
const isWindows = process.platform === 'win32'
const npmCommand = isWindows ? 'npm.cmd' : 'npm';
let installerEnabled = false;
let settings;
const moduleRe = /^(@[^/@]+?[/])?[^/@]+?$/;
const slashRe = process.platform === "win32" ? /\\|[/]/ : /[/]/;
const slashRe = isWindows ? /\\|[/]/ : /[/]/;
const pkgurlRe = /^(https?|git(|\+https?|\+ssh|\+file)):\/\//;
const localtgzRe = /^([a-zA-Z]:|\/).+tgz$/;
@@ -227,7 +230,7 @@ async function installModule(module,version,url) {
let extraArgs = triggerPayload.args || [];
let args = ['install', ...extraArgs, installName]
log.trace(npmCommand + JSON.stringify(args));
return exec.run(npmCommand,args,{ cwd: installDir}, true)
return exec.run(npmCommand,args,{ cwd: installDir, shell: true }, true)
} else {
log.trace("skipping npm install");
}
@@ -262,7 +265,7 @@ async function installModule(module,version,url) {
log.warn("------------------------------------------");
e = new Error(log._("server.install.install-failed")+": "+err.toString());
if (err.hook === "postInstall") {
return exec.run(npmCommand,["remove",module],{ cwd: installDir}, false).finally(() => {
return exec.run(npmCommand,["remove",module],{ cwd: installDir, shell: true }, false).finally(() => {
throw e;
})
}
@@ -366,7 +369,7 @@ async function getModuleVersionFromNPM(module, version) {
}
return new Promise((resolve, reject) => {
child_process.execFile(npmCommand,['info','--json',installName],function(err,stdout,stderr) {
child_process.execFile(npmCommand,['info','--json',installName],{ shell: true },function(err,stdout,stderr) {
try {
if (!stdout) {
log.warn(log._("server.install.install-failed-not-found",{name:module}));
@@ -525,7 +528,7 @@ function uninstallModule(module) {
let extraArgs = triggerPayload.args || [];
let args = ['remove', ...extraArgs, module]
log.trace(npmCommand + JSON.stringify(args));
return exec.run(npmCommand,args,{ cwd: installDir}, true)
return exec.run(npmCommand,args,{ cwd: installDir, shell: true }, true)
} else {
log.trace("skipping npm uninstall");
}
@@ -592,7 +595,7 @@ async function checkPrereq() {
installerEnabled = false;
} else {
return new Promise(resolve => {
child_process.execFile(npmCommand,['-v'],function(err,stdout) {
child_process.execFile(npmCommand,['-v'],{ shell: true },function(err,stdout) {
if (err) {
log.info(log._("server.palette-editor.npm-not-found"));
installerEnabled = false;

View File

@@ -88,7 +88,7 @@ function generateSubflowConfig(subflow) {
this.credentials['has_' + prop.name] = (this.credentials[prop.name] !== "");
} else {
switch(prop.type) {
case "str": this[prop.name] = prop.value||""; break;
case "str": case "conf-type": this[prop.name] = prop.value||""; break;
case "bool": this[prop.name] = (typeof prop.value === 'boolean')?prop.value:prop.value === "true" ; break;
case "num": this[prop.name] = (typeof prop.value === 'number')?prop.value:Number(prop.value); break;
default:

View File

@@ -20,7 +20,7 @@
"clone": "2.1.2",
"fs-extra": "11.1.1",
"semver": "7.5.4",
"tar": "6.1.13",
"tar": "6.2.1",
"uglify-js": "3.17.4"
}
}

View File

@@ -15,6 +15,7 @@
**/
const clone = require("clone");
const jsonClone = require("rfdc")();
const Flow = require('./Flow').Flow;
const context = require('../nodes/context');
const util = require("util");
@@ -108,7 +109,7 @@ class Subflow extends Flow {
}
}
subflowInternalFlowConfig.subflows = clone(subflowDef.subflows || {});
subflowInternalFlowConfig.subflows = jsonClone(subflowDef.subflows || {});
remapSubflowNodes(subflowInternalFlowConfig.configs,node_map);
remapSubflowNodes(subflowInternalFlowConfig.nodes,node_map);
@@ -220,7 +221,7 @@ class Subflow extends Flow {
}
if (this.subflowDef.in) {
subflowInstanceConfig.wires = this.subflowDef.in.map(function(n) { return n.wires.map(function(w) { return self.node_map[w.id].id;})})
subflowInstanceConfig._originalWires = clone(subflowInstanceConfig.wires);
subflowInstanceConfig._originalWires = jsonClone(subflowInstanceConfig.wires);
}
this.node = new Node(subflowInstanceConfig);
@@ -244,14 +245,14 @@ class Subflow extends Flow {
if (self.subflowDef.out) {
var node,wires,i,j;
// Restore the original wiring to the internal nodes
subflowInstanceConfig.wires = clone(subflowInstanceConfig._originalWires);
subflowInstanceConfig.wires = jsonClone(subflowInstanceConfig._originalWires);
for (i=0;i<self.subflowDef.out.length;i++) {
wires = self.subflowDef.out[i].wires;
for (j=0;j<wires.length;j++) {
if (wires[j].id != self.subflowDef.id) {
node = self.node_map[wires[j].id];
if (node && node._originalWires) {
node.wires = clone(node._originalWires);
node.wires = jsonClone(node._originalWires);
}
}
}
@@ -300,7 +301,7 @@ class Subflow extends Flow {
var node = self.node_map[wires[j].id];
if (node) {
if (!node._originalWires) {
node._originalWires = clone(node.wires);
node._originalWires = jsonClone(node.wires);
}
node.wires[wires[j].port] = (node.wires[wires[j].port]||[]).concat(this.subflowInstance.wires[i]);
} else {
@@ -323,7 +324,7 @@ class Subflow extends Flow {
var node = self.node_map[wires[j].id];
if (node) {
if (!node._originalWires) {
node._originalWires = clone(node.wires);
node._originalWires = jsonClone(node.wires);
}
node.wires[wires[j].port] = (node.wires[wires[j].port]||[]);
node.wires[wires[j].port].push(subflowStatusId);
@@ -463,7 +464,7 @@ class Subflow extends Flow {
* @return {[type]} [description]
*/
function createNodeInSubflow(subflowInstanceId, def) {
let node = clone(def);
let node = jsonClone(def);
let nid = `${subflowInstanceId}-${node.id}` //redUtil.generateId();
// console.log("Create Node In subflow",node._alias, "--->",nid, "(",node.type,")")
// node_map[node.id] = node;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
**/
var clone = require("clone");
const jsonClone = require("rfdc")();
var Flow = require('./Flow');
@@ -140,16 +140,16 @@ function setFlows(_config,_credentials,type,muteLog,forceStart,user) {
if (type === "load") {
isLoad = true;
configSavePromise = loadFlows().then(function(_config) {
config = clone(_config.flows);
newFlowConfig = flowUtil.parseConfig(clone(config));
config = jsonClone(_config.flows);
newFlowConfig = flowUtil.parseConfig(jsonClone(config));
type = "full";
return _config.rev;
});
} else {
// Clone the provided config so it can be manipulated
config = clone(_config);
config = jsonClone(_config);
// Parse the configuration
newFlowConfig = flowUtil.parseConfig(clone(config));
newFlowConfig = flowUtil.parseConfig(jsonClone(config));
// Generate a diff to identify what has changed
diff = flowUtil.diffConfigs(activeFlowConfig,newFlowConfig);
@@ -609,7 +609,7 @@ async function addFlow(flow, user) {
nodes.push(node);
}
}
var newConfig = clone(activeConfig.flows);
var newConfig = jsonClone(activeConfig.flows);
newConfig = newConfig.concat(nodes);
return setFlows(newConfig, null, 'flows', true, null, user).then(function() {
@@ -650,7 +650,7 @@ function getFlow(id) {
var nodeIds = Object.keys(flow.nodes);
if (nodeIds.length > 0) {
result.nodes = nodeIds.map(function(nodeId) {
var node = clone(flow.nodes[nodeId]);
var node = jsonClone(flow.nodes[nodeId]);
if (node.type === 'link out') {
delete node.wires;
}
@@ -662,7 +662,7 @@ function getFlow(id) {
if (flow.configs) {
var configIds = Object.keys(flow.configs);
result.configs = configIds.map(function(configId) {
const node = clone(flow.configs[configId]);
const node = jsonClone(flow.configs[configId]);
delete node.credentials;
return node
@@ -674,17 +674,17 @@ function getFlow(id) {
if (flow.subflows) {
var subflowIds = Object.keys(flow.subflows);
result.subflows = subflowIds.map(function(subflowId) {
var subflow = clone(flow.subflows[subflowId]);
var subflow = jsonClone(flow.subflows[subflowId]);
var nodeIds = Object.keys(subflow.nodes);
subflow.nodes = nodeIds.map(function(id) {
const node = clone(subflow.nodes[id])
const node = jsonClone(subflow.nodes[id])
delete node.credentials
return node
});
if (subflow.configs) {
var configIds = Object.keys(subflow.configs);
subflow.configs = configIds.map(function(id) {
const node = clone(subflow.configs[id])
const node = jsonClone(subflow.configs[id])
delete node.credentials
return node
})
@@ -709,7 +709,7 @@ async function updateFlow(id,newFlow, user) {
}
label = activeFlowConfig.flows[id].label;
}
var newConfig = clone(activeConfig.flows);
var newConfig = jsonClone(activeConfig.flows);
var nodes;
if (id === 'global') {
@@ -779,7 +779,7 @@ async function removeFlow(id, user) {
throw e;
}
var newConfig = clone(activeConfig.flows);
var newConfig = jsonClone(activeConfig.flows);
newConfig = newConfig.filter(function(node) {
return node.z !== id && node.id !== id;
});

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
const clone = require("clone");
const jsonClone = require("rfdc")();
const redUtil = require("@node-red/util").util;
const Log = require("@node-red/util").log;
const typeRegistry = require("@node-red/registry");
@@ -68,7 +68,7 @@ function mapEnvVarProperties(obj,prop,flow,config) {
if (obj[prop][0] === "$" && (EnvVarPropertyRE_old.test(v) || EnvVarPropertyRE.test(v)) ) {
const envVar = v.substring(2,v.length-1);
const r = redUtil.getSetting(config, envVar, flow);
if (r !== undefined && r !== '') {
if (r !== undefined) {
obj[prop] = r
}
}
@@ -175,7 +175,7 @@ async function createNode(flow,config) {
try {
var nodeTypeConstructor = typeRegistry.get(type);
if (typeof nodeTypeConstructor === "function") {
var conf = clone(config);
var conf = jsonClone(config);
delete conf.credentials;
try {
Object.defineProperty(conf,'_module', {value: typeRegistry.getNodeInfo(type), enumerable: false, writable: true })
@@ -202,8 +202,8 @@ async function createNode(flow,config) {
var subflowInstanceConfig = subflowConfig.subflows[nodeTypeConstructor.subflow.id];
delete subflowConfig.subflows[nodeTypeConstructor.subflow.id];
subflowInstanceConfig.subflows = subflowConfig.subflows;
var instanceConfig = clone(config);
instanceConfig.env = clone(nodeTypeConstructor.subflow.env);
var instanceConfig = jsonClone(config);
instanceConfig.env = jsonClone(nodeTypeConstructor.subflow.env);
instanceConfig.env = nodeTypeConstructor.subflow.env.map(nodeProp => {
var nodePropType;
@@ -256,7 +256,7 @@ function parseConfig(config) {
flow.missingTypes = [];
config.forEach(function (n) {
flow.allNodes[n.id] = clone(n);
flow.allNodes[n.id] = jsonClone(n);
if (n.type === 'tab') {
flow.flows[n.id] = n;
flow.flows[n.id].subflows = {};

View File

@@ -14,9 +14,8 @@
* limitations under the License.
**/
var path = require("path");
var fs = require("fs");
var clone = require("clone");
const jsonClone = require("rfdc")();
var util = require("util");
var registry = require("@node-red/registry");
@@ -98,7 +97,7 @@ function createNode(node,def) {
}
var creds = credentials.get(id);
if (creds) {
creds = clone(creds);
creds = jsonClone(creds);
//console.log("Attaching credentials to ",node.id);
// allow $(foo) syntax to substitute env variables for credentials also...
for (var p in creds) {

View File

@@ -25,6 +25,7 @@
"removing-modules": "設定からモジュールを削除します",
"added-types": "追加したノード:",
"removed-types": "削除したノード:",
"removed-plugins": "削除したプラグイン:",
"install": {
"invalid": "不正なモジュール名",
"installing": "モジュール __name__, バージョン: __version__ をインストールします",

View File

@@ -22,6 +22,7 @@
"clone": "2.1.2",
"express": "4.19.2",
"fs-extra": "11.1.1",
"json-stringify-safe": "5.0.1"
"json-stringify-safe": "5.0.1",
"rfdc": "^1.3.1"
}
}