diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 8f3c8a6ce..85fc1f92a 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -5,6 +5,9 @@ on:
release:
types: [published]
+permissions:
+ contents: read
+
jobs:
generate:
name: 'Update node-red-docker image'
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 0db909da6..b7f54c5f1 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -6,8 +6,14 @@ on:
pull_request:
branches: [ master, dev ]
+permissions:
+ contents: read
+
jobs:
build:
+ permissions:
+ checks: write # for coverallsapp/github-action to create new checks
+ contents: read # for actions/checkout to fetch code
runs-on: ubuntu-latest
strategy:
matrix:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6c6f52aeb..872c40e7f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,127 @@
+#### 3.1.0-beta.1: Beta Release
+
+Editor
+
+ - NEW: Locking Flows (#3938) @knolleary
+ - NEW: Improve UX around hiding flows via context menu (#3930) @knolleary
+ - NEW: Add support for inline image in markdown editor by drag and drop of an image file (#4006) @HiroyasuNishiyama
+ - NEW: Add support for mermaid diagram to markdown editor (#4007) @HiroyasuNishiyama
+ - NEW: Support uri fragments for nodes and groups including edit support (#3870) @knolleary
+
+ - Remember compact/pretty flow export user choice (#3974) @Steve-Mcl
+ - fix .red-ui-notification class (#4035) @xiaobinqt
+ - Fix border radius on Modules list header (#4038) @bonanitech
+ - fix workspace reference error in case of empty tabs (#4029) @HiroyasuNishiyama
+ - Disable delete tab menu when single tab exists (#4030) @HiroyasuNishiyama
+ - Disable hide all menu if all tabs hidden (#4031) @HiroyasuNishiyama
+ - fix hide subflow tooltip (#4033) @HiroyasuNishiyama
+ - Fix disabled menu items in project feature (#4027) @kazuhitoyokoi
+ - Let themes change radialMenu text colors (#3995) @bonanitech
+ - Add Japanese translations for v3.0.3 (#4012) @kazuhitoyokoi
+ - Add Japanese translation for v3.1.0-beta.0 (#3997) @kazuhitoyokoi
+ - Add Japanese translation for v3.1.0-beta.0 (#3916) @kazuhitoyokoi
+ - Hide subflow category after deleting subflow (#3980) @kazuhitoyokoi
+ - Prevent dbl-click opening node edit dialog with text selected (#3970) @knolleary
+ - Handle replacing unknown node inside group or subflow (#3921) @knolleary
+ - Fix #3939, red border red-ui-typedInput-container (#3949) @Steveorevo
+ - i18n item URL copy notification & add Japanese message (#3946) @HiroyasuNishiyama
+ - add Japanese message for item url copy actions (#3947) @HiroyasuNishiyama
+ - Fix autocomplete entry for responseUrl (#3884) @knolleary
+ - Fix Japanese translation for JSONata editor (#3872) @HiroyasuNishiyama
+ - Fix search type with spaces (#3841) @Steve-Mcl
+ - Fix error hanndling of JSONata expression editor for extended functions (#3871) @HiroyasuNishiyama
+ - Add button type to the adding SSH key button (#3866) @kazuhitoyokoi
+ - Check radio button as default in project dialog (#3879) @kazuhitoyokoi
+ - Add $clone as supported function (#3874) @HiroyasuNishiyama
+ - Env var jsonata (#3807) @HiroyasuNishiyama
+ - Add Japanese translation for v3.0.2 (#3852) @kazuhitoyokoi
+
+Runtime
+
+ - NEW: Add global environment variable feature (#3941) @HiroyasuNishiyama
+ - Force IPv4 name resolution to have priority (#4019) @dceejay
+ - Fix async loading of modules containing both nodes and plugins (#3999) @knolleary
+ - Use main branch as default in project feature (#4036) @kazuhitoyokoi
+ - Rename package var to avoid strict mode error (#4020) @knolleary
+ - Fix typos in settings.js (#4013) @ypid
+ - Ensure credentials object is removed before returning node in getFlow request (#3971) @knolleary
+ - Ignore commit error in project feature (#3987) @kazuhitoyokoi
+ - Update dependencies (#3969) @knolleary
+ - Add check that node sends object rather than primitive type (#3909) @knolleary
+ - Ensure key_path is quoted in GIT_SSH_COMMAND in case of spaces in pathname (#3912) @knolleary
+ - Fix nodesDir scan when node package has js/html in sub dir to package.json (#3867) @Steve-Mcl
+ - Fix file permissions (#3917) @kazuhitoyokoi
+ - ci: add minimum GitHub token permissions for workflows (#3907) @boahc077
+
+Nodes
+
+ - Catch: fix typo in catch.html (#3965) @we11adam
+ - Change: Fix change node overwriting msg with itself (#3899) @dceejay
+ - Comment node: Clarify where the text will appear (#4004) @dirkjanfaber
+ - CSV: change replace to replaceAll (#3990) @dceejay
+ - CSV node: check header properties for ' and " (#3920) @dceejay
+ - CSV: Fix for CSV undefined property (#3906) @dceejay
+ - Delay: let delay node handle both flush then reset (#3898) @dceejay
+ - Function: Limit number of ports in function node (#3886) @kazuhitoyokoi
+ - Function: Remove dot from variable name for external module in function node (#3880) @kazuhitoyokoi
+ - Function: add function node monaco types util and promisify (#3868) @Steve-Mcl
+ - HTTP In: Ensure msg.req.headers is enumerable (#3908) @knolleary
+ - HTTP Request: Support form-data arrays (#3991) @hardillb
+ - HTTP Request: Fix httprequest tests to be more lenient on error message (#3922) @knolleary
+ - HTTP Request: Add missing property to node object HTTPRequest (#3842) @hardillb
+ - HTTP Request/Response: Support sortable list on property UI of http request and http response nodes (#3857) @kazuhitoyokoi
+ - HTTP Response: Ensure statusCode is a number (#3894) @hardillb
+ - Inject: Allow Inject node to work with async context stores (#4021) @knolleary
+ - Join/Batch: Add count to join and batch node labels (#4028) @dceejay
+ - MQTT: Fix birth topic handling in MQTT node (#3905) @Steve-Mcl
+ - MQTT: Fix pull-down menus of MQTT configuration node (#3890) @kazuhitoyokoi
+ - MQTT: Prevent invalid mqtt birth topic crashing node-red (#3869) @Steve-Mcl
+ - MQTT: ensure sessionExpiry(Interval) is applied (#3840) @Steve-Mcl
+ - MQTT: Fix mqtt nodes not reconnecting on modified-flows deploy (#3992) @knolleary
+ - MQTT: fix single subscription mqtt node status (#3966) @Steve-Mcl
+ - Range: Add drop mode to range node (#3935) @dceejay
+ - Remove done from describe (#3873) @HiroyasuNishiyama
+ - Split node: avoid duplicate done call for buffer split (#4000) @knolleary
+ - Status: Fix typo in 25-status.html (#3981) @kazuhitoyokoi
+ - TCP Node: ensure newline substitution applies to whole message (#4009) @dceejay
+ - Template: Add information about environment variable to template node (#3882) @kazuhitoyokoi
+ - Trigger: Hide trigger node repeat send option if sending nothing (#4023) @dceejay
+ - Watch: fix watch node test on MacOS/ARM (#3942) @HiroyasuNishiyama
+
+#### 3.0.2: Maintenance Release
+
+Editor
+
+ - Fix workspace chart bottom property (#3812) @bonanitech
+ - Update german translation (#3802) @Dennis14e
+ - Support color reset to the default in subflow and group (#3801) @kazuhitoyokoi
+ - Allow generateNodeNames to handle names containing regex control chars (#3817) @knolleary
+ - Hide scrollbars until they're needed (#3808) @bonanitech
+ - Include junctions/groups when exporting subflows plus related fixes (#3816) @knolleary
+ - remove console.log (#3820) @Steve-Mcl
+
+Runtime
+
+ - Register subflow module instance node with parent flow (#3818) @knolleary
+
+Nodes
+
+ - HTTP Request: Allow HTTP Headers not in spec (#3776) @hardillb
+
+#### 3.0.1: Maintenance Release
+
+Editor
+
+ - Allow codeEditor theme to be set even if `codeEditor` is not set in settings.js (#3794) @Steve-Mcl
+ - Sys info (diagnostics report) amendments (#3793) @Steve-Mcl
+ - Allow `mode` and `title` to be omitted in `options` argument for `createEditor` (#3791) @Steve-Mcl
+ - Fix focus issues (#3789) @Steve-Mcl
+ - Ensure all typedInput buttons have button type set (#3788) @knolleary
+ - Do not flag hasUsers=false nodes as unused in search (#3787) @knolleary
+ - Properly position quick-add dialog in all cases (#3786) @knolleary
+ - Ensure quick-add dialog does not obscure ghost node when shifted (#3785) @knolleary
+ - Remove use of Object.hasOwn (#3784) @knolleary
+
#### 3.0.0: Milestone Release
Editor
diff --git a/Gruntfile.js b/Gruntfile.js
index 2f81da923..dcb96a2c0 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -151,6 +151,7 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/font-awesome.js",
"packages/node_modules/@node-red/editor-client/src/js/history.js",
"packages/node_modules/@node-red/editor-client/src/js/validators.js",
+ "packages/node_modules/@node-red/editor-client/src/js/ui/mermaid.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/utils.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js",
@@ -169,6 +170,7 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/ui/diagnostics.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/diff.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/keyboard.js",
+ "packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/statusBar.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view.js",
@@ -224,7 +226,7 @@ module.exports = function(grunt) {
"node_modules/jsonata/jsonata-es5.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jsonata/formatter.js",
"packages/node_modules/@node-red/editor-client/src/vendor/ace/ace.js",
- "packages/node_modules/@node-red/editor-client/src/vendor/ace/ext-language_tools.js",
+ "packages/node_modules/@node-red/editor-client/src/vendor/ace/ext-language_tools.js"
],
// "packages/node_modules/@node-red/editor-client/public/vendor/vendor.css": [
// // TODO: resolve relative resource paths in
@@ -233,6 +235,9 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/public/vendor/ace/worker-jsonata.js": [
"node_modules/jsonata/jsonata-es5.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jsonata/worker-jsonata.js"
+ ],
+ "packages/node_modules/@node-red/editor-client/public/vendor/mermaid/mermaid.min.js": [
+ "node_modules/mermaid/dist/mermaid.min.js"
]
}
}
diff --git a/package.json b/package.json
index b596e10b6..ad6ccd4db 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "node-red",
- "version": "3.1.0-beta.0",
+ "version": "3.1.0-beta.1",
"description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org",
"license": "Apache-2.0",
@@ -26,13 +26,13 @@
}
],
"dependencies": {
- "acorn": "8.7.1",
+ "acorn": "8.8.1",
"acorn-walk": "8.2.0",
- "ajv": "8.11.0",
- "async-mutex": "0.3.2",
+ "ajv": "8.11.2",
+ "async-mutex": "0.4.0",
"basic-auth": "2.0.1",
"bcryptjs": "2.4.3",
- "body-parser": "1.20.0",
+ "body-parser": "1.20.1",
"cheerio": "1.0.0-rc.10",
"clone": "2.1.2",
"content-type": "1.0.4",
@@ -40,16 +40,16 @@
"cookie-parser": "1.4.6",
"cors": "2.8.5",
"cronosjs": "1.7.1",
- "denque": "2.0.1",
- "express": "4.18.1",
+ "denque": "2.1.0",
+ "express": "4.18.2",
"express-session": "1.17.3",
"form-data": "4.0.0",
"fs-extra": "10.1.0",
"got": "11.8.5",
"hash-sum": "2.0.0",
- "hpagent": "1.0.0",
+ "hpagent": "1.2.0",
"https-proxy-agent": "5.0.1",
- "i18next": "21.8.14",
+ "i18next": "21.10.0",
"iconv-lite": "0.6.3",
"is-utf8": "0.2.1",
"js-yaml": "4.1.0",
@@ -60,7 +60,7 @@
"memorystore": "1.6.7",
"mime": "3.0.0",
"moment": "2.29.4",
- "moment-timezone": "0.5.34",
+ "moment-timezone": "0.5.39",
"mqtt": "4.3.7",
"multer": "1.4.5-lts.1",
"mustache": "4.2.0",
@@ -73,19 +73,19 @@
"passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2",
"raw-body": "2.5.1",
- "semver": "7.3.7",
- "tar": "6.1.11",
- "tough-cookie": "4.0.0",
- "uglify-js": "3.16.2",
+ "semver": "7.3.8",
+ "tar": "6.1.12",
+ "tough-cookie": "4.1.2",
+ "uglify-js": "3.17.4",
"uuid": "8.3.2",
"ws": "7.5.6",
"xml2js": "0.4.23"
},
"optionalDependencies": {
- "bcrypt": "5.0.1"
+ "bcrypt": "5.1.0"
},
"devDependencies": {
- "dompurify": "2.3.9",
+ "dompurify": "2.4.1",
"grunt": "1.5.3",
"grunt-chmod": "~1.1.1",
"grunt-cli": "~1.4.3",
@@ -108,13 +108,14 @@
"i18next-http-backend": "1.4.1",
"jquery-i18next": "1.2.1",
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
- "marked": "4.0.18",
+ "marked": "4.2.3",
+ "mermaid": "^9.3.0",
"minami": "1.2.3",
"mocha": "9.2.2",
"node-red-node-test-helper": "^0.3.0",
- "nodemon": "2.0.19",
+ "nodemon": "2.0.20",
"proxy": "^1.0.2",
- "sass": "1.53.0",
+ "sass": "1.56.1",
"should": "13.2.3",
"sinon": "11.1.2",
"stoppable": "^1.1.0",
diff --git a/packages/node_modules/@node-red/editor-api/lib/editor/theme.js b/packages/node_modules/@node-red/editor-api/lib/editor/theme.js
index 7be8868d3..c21a7e6e7 100644
--- a/packages/node_modules/@node-red/editor-api/lib/editor/theme.js
+++ b/packages/node_modules/@node-red/editor-api/lib/editor/theme.js
@@ -327,9 +327,8 @@ module.exports = {
themeContext.header.url = themePlugin.header.url || themeContext.header.url
}
}
- if(theme.codeEditor) {
- theme.codeEditor.options = Object.assign({}, themePlugin.monacoOptions, theme.codeEditor.options);
- }
+ theme.codeEditor = theme.codeEditor || {}
+ theme.codeEditor.options = Object.assign({}, themePlugin.monacoOptions, theme.codeEditor.options);
}
activeThemeInitialised = true;
}
diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json
index dd7020e75..920104803 100644
--- a/packages/node_modules/@node-red/editor-api/package.json
+++ b/packages/node_modules/@node-red/editor-api/package.json
@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-api",
- "version": "3.1.0-beta.0",
+ "version": "3.1.0-beta.1",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,14 +16,14 @@
}
],
"dependencies": {
- "@node-red/util": "3.1.0-beta.0",
- "@node-red/editor-client": "3.1.0-beta.0",
+ "@node-red/util": "3.1.0-beta.1",
+ "@node-red/editor-client": "3.1.0-beta.1",
"bcryptjs": "2.4.3",
- "body-parser": "1.20.0",
+ "body-parser": "1.20.1",
"clone": "2.1.2",
"cors": "2.8.5",
"express-session": "1.17.3",
- "express": "4.18.1",
+ "express": "4.18.2",
"memorystore": "1.6.7",
"mime": "3.0.0",
"multer": "1.4.5-lts.1",
@@ -35,6 +35,6 @@
"ws": "7.5.6"
},
"optionalDependencies": {
- "bcrypt": "5.0.1"
+ "bcrypt": "5.1.0"
}
}
diff --git a/packages/node_modules/@node-red/editor-client/locales/de/editor.json b/packages/node_modules/@node-red/editor-client/locales/de/editor.json
old mode 100755
new mode 100644
index 41fe1459a..93a2f5946
--- a/packages/node_modules/@node-red/editor-client/locales/de/editor.json
+++ b/packages/node_modules/@node-red/editor-client/locales/de/editor.json
@@ -105,7 +105,7 @@
"search": "Flows durchsuchen",
"searchInput": "Flows durchsuchen",
"subflows": "Subflow",
- "createSubflow": "Subflow",
+ "createSubflow": "Hinzufügen",
"selectionToSubflow": "Auswahl in Subflow umwandeln",
"flows": "Flow",
"add": "Hinzufügen",
@@ -152,7 +152,8 @@
"zoom-in": "Vergrößern",
"search-flows": "Flows durchsuchen",
"search-prev": "Vorherige",
- "search-next": "Nächste"
+ "search-next": "Nächste",
+ "search-counter": "\"__term__\" __result__ von __count__"
},
"user": {
"loggedInAs": "Angemeldet als __name__",
@@ -168,7 +169,11 @@
}
},
"notification": {
- "warning": "Warnung: __message__",
+ "state": {
+ "flowsStopped": "Flows gestoppt",
+ "flowsStarted": "Flows gestartet"
+ },
+ "warning": "Warnung : __message__",
"warnings": {
"undeployedChanges": "Node hat nicht übernommene (deploy) Änderungen",
"nodeActionDisabled": "Node-Aktionen deaktiviert",
@@ -177,15 +182,15 @@
"missing-modules": "
Flows angehalten aufgrund fehlender Module
",
"safe-mode": "Flows sind im abgesicherten Modus gestoppt.
Flows können bearbeitet und übernommen (deploy) werden, um sie neu zu starten.
",
"restartRequired": "Node-RED muss neu gestartet werden, damit die Module nach Upgrade aktiviert werden",
- "credentials_load_failed": "Flows gestoppt, da die Berechtigungen nicht entschlüsselt werden konnten.
Die Datei mit dem Flow-Berechtigungen ist verschlüsselt, aber der Schlüssel des Projekts fehlt oder ist ungültig.
",
- "credentials_load_failed_reset": "Die Berechtigungen konnten nicht entschlüsselt werden.
Die Datei mit den Flow-Berechtigungen ist verschlüsselt, aber der Schlüssel des Projekts fehlt oder ist ungültig.
Die Datei mit den Flow-Berechtigungen wird bei der nächsten Übernahme (deploy) zurückgesetzt. Alle vorhandenen Flow-Berechtigungen werden gelöscht.
",
+ "credentials_load_failed": "Flows gestoppt, da die Credentials nicht entschlüsselt werden konnten.
Die Datei mit den Flow-Credentials ist verschlüsselt, aber der Schlüssel des Projekts fehlt oder ist ungültig.
",
+ "credentials_load_failed_reset": "Die Credentials konnten nicht entschlüsselt werden.
Die Datei mit den Flow-Credentials ist verschlüsselt, aber der Schlüssel des Projekts fehlt oder ist ungültig.
Die Datei mit den Flow-Credentials wird bei der nächsten Übernahme (deploy) zurückgesetzt. Alle vorhandenen Flow-Credentials werden gelöscht.
",
"missing_flow_file": "Die Flow-Datei des Projekts wurde nicht gefunden.
Das Projekt ist nicht mit einer Flow-Datei konfiguriert.
",
"missing_package_file": "Die Paket-Datei des Projekts wurde nicht gefunden.
In dem Projekt fehlt die 'package.json'-Datei.
",
"project_empty": "Das Projekt ist leer.
Soll ein Standardsatz an Projektdateien erstellen werden? Andernfalls müssen die Dateien manuell außerhalb des Editors dem Projekt hinzugefügt werden.
",
"project_not_found": "Das Projekt '__project__' wurde nicht gefunden.
",
"git_merge_conflict": "Der automatische Merge der Änderungen ist fehlgeschlagen.
Die Merge-Konflikte müssen behoben und die Ergebnisse ins Repository übertragen werden (commit).
"
},
- "error": "Fehler: __message__",
+ "error": "Fehler : __message__",
"errors": {
"lostConnection": "Verbindung zum Server verloren. Verbindung wird erneut hergestellt ...",
"lostConnectionReconnect": "Verbindung zum Server verloren. Wiederherstellung der Verbindung in __time__s.",
@@ -203,7 +208,7 @@
"pull": "Projekt '__project__' erneut geladen",
"revert": "Änderungen im Projekt '__project__' rückgängig gemacht",
"merge-complete": "Git-Merge abgeschlossen",
- "setupCredentials": "Berechtigungen einrichten",
+ "setupCredentials": "Credentials einrichten",
"setupProjectFiles": "Projektdateien einrichten",
"no": "Nein, Danke",
"createDefault": "Standardprojektdateien erstellen",
@@ -211,7 +216,7 @@
},
"label": {
"manage-project-dep": "Projektabhängigkeiten verwalten",
- "setup-cred": "Berechtigungen einrichten",
+ "setup-cred": "Credentials einrichten",
"setup-project": "Projektdateien einrichten",
"create-default-package": "Standardpaketdatei erstellen",
"no-thanks": "Nein, Danke",
@@ -295,6 +300,10 @@
"modifiedFlowsDesc": "Übernimmt nur Flows, die geänderte Nodes enthalten",
"modifiedNodes": "Geänderte Nodes",
"modifiedNodesDesc": "Übernimmt nur Nodes, die sich geändert haben",
+ "startFlows": "Start",
+ "startFlowsDesc": "Flows starten",
+ "stopFlows": "Stop",
+ "stopFlowsDesc": "Flows stoppen",
"restartFlows": "Flows neustarten",
"restartFlowsDesc": "Startet die aktuell übernommenen Flows (ohne vorheriges Deploy)",
"successfulDeploy": "Erfolgreich übernommen (deploy)",
@@ -376,7 +385,7 @@
"confirmDelete": "Sind Sie sicher mit dem Löschen dieses Subflows?",
"info": "Beschreibung",
"category": "Kategorie",
- "module": "Module",
+ "module": "Modul",
"license": "Lizenz",
"licenseNone": "Keine",
"licenseOther": "Andere",
@@ -434,7 +443,7 @@
"icon": "Icon",
"inputType": "Eingangstyp",
"selectType": "Wähle Typen ...",
- "loadCredentials": "Lade Node-Berechtigungen",
+ "loadCredentials": "Lade Node-Credentials",
"inputs": {
"input": "Eingang",
"select": "Auswahl",
@@ -450,7 +459,7 @@
"json": "JSON",
"bin": "buffer",
"env": "Umgebungsvariable",
- "cred": "Berechtigung"
+ "cred": "Credentials"
},
"menu": {
"input": "Eingang",
@@ -470,7 +479,7 @@
"errors": {
"scopeChange": "Wenn Sie den Geltungsbereich (scope) ändern, wird er für Nodes in anderen Flows nicht verfügbar sein",
"invalidProperties": "Ungültige Eigenschaften:",
- "credentialLoadFailed": "Laden der Node-Berechtigungen fehlgeschlagen"
+ "credentialLoadFailed": "Laden der Node-Credentials fehlgeschlagen"
}
},
"keyboard": {
@@ -683,7 +692,8 @@
"showHelp": "Hilfe zeigen",
"showInOutline": "Zeige im Editor",
"showTopics": "Zeige Hilfethemen",
- "noHelp": "Kein Hilfethema ausgewählt"
+ "noHelp": "Kein Hilfethema ausgewählt",
+ "changeLog": "Änderungsprotokoll"
},
"config": {
"name": "Konfigurations-Node",
@@ -737,7 +747,7 @@
"addToProject": "Zu Projekt hinzufügen",
"files": "Dateien",
"flow": "Flow",
- "credentials": "Berechtigungen",
+ "credentials": "Credentials",
"package": "Paket",
"packageCreate": "Datei wird erstellt beim Speichern der Änderungen",
"fileNotExist": "Datei existiert nicht",
@@ -750,7 +760,7 @@
"changeTheEncryptionKey": "Schlüssel ändern",
"currentKey": "Aktueller Schlüssel",
"newKey": "Neuer Schlüssel",
- "credentialsAlert": "Dadurch werden alle vorhandenen Berechtigungen gelöscht",
+ "credentialsAlert": "Dadurch werden alle vorhandenen Credentials gelöscht",
"versionControl": "Versionsverwaltung (Git)",
"branches": "Branches",
"noBranches": "Keine Branches",
@@ -886,7 +896,7 @@
"date": "timestamp",
"jsonata": "JSONata",
"env": "Umgebungsvariable",
- "cred": "Berechtigung"
+ "cred": "Credentials"
}
},
"editableList": {
@@ -1026,7 +1036,7 @@
"passphrase": "Passphrase",
"ssh-key-desc": "Bevor Sie ein Repository über SSH lokal klonen können, müssen Sie einen SSH-Schlüssel hinzufügen, um auf diesen zugreifen zu können",
"ssh-key-add": "SSH-Schlüssel hinzufügen",
- "credential-key": "Schlüssel für Berechtigungen",
+ "credential-key": "Schlüssel für Credentials",
"cant-get-ssh-key": "Fehler! Der ausgewählte SSH-Schlüsselpfad kann nicht abgerufen werden",
"already-exists2": "bereits vorhanden",
"git-error": "Git-Fehler",
@@ -1038,27 +1048,27 @@
"create": "Erstellen Sie Ihre Projektdateien",
"desc0": "Ein Projekt enthält Ihre Flow-Dateien, eine README-Datei und die 'package.json'-Datei.",
"desc1": "Es kann alle anderen Dateien enthalten, die im Git-Repository verwaltet werden sollen.",
- "desc2": "Ihre vorhandenen Flow- und Berechtigungs-Dateien werden in das Projekt kopiert.",
+ "desc2": "Ihre vorhandenen Flow- und Credential-Dateien werden in das Projekt kopiert.",
"flow-file": "Flow-Datei",
- "credentials-file": "Datei mit Berechtigungen"
+ "credentials-file": "Datei mit Credentials"
},
"encryption-config": {
- "setup": "Einrichtung der Verschlüsselung Ihrer Datei mit den Berechtigungen",
- "desc0": "Die Datei mit den Flow-Berechtigungen kann verschlüsselt werden, um ihren Inhalt zu schützen.",
- "desc1": "Wenn Sie diese Berechtigungen in einem öffentlichen Repository speichern möchten, müssen Sie sie mit einen geheimen Schlüsselausdruck verschlüsseln.",
- "desc2": "Die Datei mit den Flow-Berechtigungen ist derzeit nicht verschlüsselt.",
+ "setup": "Einrichtung der Verschlüsselung Ihrer Datei mit den Credentials",
+ "desc0": "Die Datei mit den Flow-Credentials kann verschlüsselt werden, um ihren Inhalt zu schützen.",
+ "desc1": "Wenn Sie diese Credentials in einem öffentlichen Repository speichern möchten, müssen Sie sie mit einen geheimen Schlüsselausdruck verschlüsseln.",
+ "desc2": "Die Datei mit den Flow-Credentials ist derzeit nicht verschlüsselt.",
"desc3": "D.h. ihr Inhalt (z.B. Passwörter und Zugriffs-Tokens) kann von jedem mit Zugriff auf die Datei gelesen werden.",
- "desc4": "Wenn Sie diese Berechtigungen in einen öffentlichen Repository speichern möchten, müssen Sie diese verschlüsseln, indem Sie einen geheimen Schlüsselausdruck eingeben.",
- "desc5": "Ihre Datei mit den Flow-Berechtigungen wird derzeit mit dem Eintrag 'credentialSecret' Ihrer Einstellungsdatei als Schlüssel verschlüsselt.",
- "desc6": "Die Datei mit den Flow-Berechtigungen wird derzeit mit einem vom System generierten Schlüssel verschlüsselt. Sie sollten einen neuen geheimen Schlüssel für dieses Projekt vorgeben.",
+ "desc4": "Wenn Sie diese Credentials in einen öffentlichen Repository speichern möchten, müssen Sie diese verschlüsseln, indem Sie einen geheimen Schlüsselausdruck eingeben.",
+ "desc5": "Ihre Datei mit den Flow-Credentials wird derzeit mit dem Eintrag 'credentialSecret' Ihrer Einstellungsdatei als Schlüssel verschlüsselt.",
+ "desc6": "Die Datei mit den Flow-Credentials wird derzeit mit einem vom System generierten Schlüssel verschlüsselt. Sie sollten einen neuen geheimen Schlüssel für dieses Projekt vorgeben.",
"desc7": "Der Schlüssel wird separat von den Projektdateien gespeichert. Sie müssen den Schlüssel angeben, damit dieses Projekt auch in einem anderen Node-RED-System verwendet werden kann.",
- "credentials": "Berechtigung",
+ "credentials": "Credentials",
"enable": "Verschlüsselung aktivieren",
"disable": "Verschlüsselung deaktivieren",
"disabled": "deaktiviert",
"copy": "Vorhandenen Schlüssel ersetzen",
"use-custom": "Eigenen Schlüssel verwenden",
- "desc8": "Die Datei mit den Berechtigungen wird nicht verschlüsselt, und ihr Inhalt kann leicht gelesen werden",
+ "desc8": "Die Datei mit den Credentials wird nicht verschlüsselt und ihr Inhalt kann leicht gelesen werden",
"create-project-files": "Projektdateien erstellen",
"create-project": "Projekt erstellen",
"already-exists": "bereits vorhanden",
@@ -1083,12 +1093,12 @@
"desc": "Beschreibung",
"opt": "Optional",
"flow-file": "Flow-Datei",
- "credentials": "Berechtigungen",
+ "credentials": "Credentials",
"enable-encryption": "Verschlüsselung aktivieren",
"disable-encryption": "Verschlüsselung deaktivieren",
"encryption-key": "Schlüssel",
- "desc0": "Eine Floskel, mit der Sie Ihre Berechtigungen schützen",
- "desc1": "Die Datei mit den Berechtigungen wird nicht verschlüsselt, und ihr Inhalt kann leicht gelesen werden",
+ "desc0": "Eine Ausdruck, mit der Sie Ihre Credentials schützen",
+ "desc1": "Die Datei mit den Credentials wird nicht verschlüsselt und ihr Inhalt kann leicht gelesen werden",
"git-url": "Git-Repository-URL",
"protocols": "https://, ssh:// oder file://",
"auth-failed": "Authentifizierung fehlgeschlagen",
@@ -1098,7 +1108,7 @@
"passphrase": "Passphrase",
"desc2": "Bevor Sie ein Repository über SSH klonen können, müssen Sie einen SSH-Schlüssel hinzufügen, um auf diesen zu zugreifen",
"add-ssh-key": "Einen SSH-Schlüssel hinzufügen",
- "credentials-encryption-key": "Schlüssel für Berechtigungen",
+ "credentials-encryption-key": "Schlüssel für Credentials",
"already-exists-2": "bereits vorhanden",
"git-error": "Git-Fehler",
"con-failed": "Verbindung fehlgeschlagen",
@@ -1156,7 +1166,8 @@
"tourGuide": {
"takeATour": "Tour starten",
"start": "Start",
- "next": "Nächste"
+ "next": "Nächste",
+ "welcomeTours": "Welcome Tours"
},
"diagnostics": {
"title": "System-Informationen"
diff --git a/packages/node_modules/@node-red/editor-client/locales/de/infotips.json b/packages/node_modules/@node-red/editor-client/locales/de/infotips.json
old mode 100755
new mode 100644
diff --git a/packages/node_modules/@node-red/editor-client/locales/de/jsonata.json b/packages/node_modules/@node-red/editor-client/locales/de/jsonata.json
old mode 100755
new mode 100644
diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
old mode 100755
new mode 100644
index 6dcbd1d75..6055c721f
--- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
+++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
@@ -53,8 +53,10 @@
"confirmDelete": "Confirm delete",
"delete": "Are you sure you want to delete '__label__'?",
"dropFlowHere": "Drop the flow here",
+ "dropImageHere": "Drop the image here",
"addFlow": "Add flow",
"addFlowToRight": "Add flow to the right",
+ "closeFlow": "Close flow",
"hideFlow": "Hide flow",
"hideOtherFlows": "Hide other flows",
"showAllFlows": "Show all flows (__count__ hidden)",
@@ -692,7 +694,8 @@
"globalConfig": "Global Configuration Nodes",
"triggerAction": "Trigger action",
"find": "Find in workspace",
- "copyItemUrl": "Copy item url"
+ "copyItemUrl": "Copy item url",
+ "copyURL2Clipboard": "Copied url to clipboard"
},
"help": {
"name": "Help",
@@ -945,6 +948,9 @@
"invalid-expr": "Invalid JSONata expression:\n __message__",
"invalid-msg": "Invalid example JSON message:\n __message__",
"context-unsupported": "Cannot test context functions\n $flowContext or $globalContext",
+ "env-unsupported": "Cannot test $env function",
+ "moment-unsupported": "Cannot test $moment function",
+ "clone-unsupported": "Cannot test $clone function",
"eval": "Error evaluating expression:\n __message__"
}
},
@@ -990,7 +996,10 @@
"quote": "Quote",
"link": "Link",
"horizontal-rule": "Horizontal rule",
- "toggle-preview": "Toggle preview"
+ "toggle-preview": "Toggle preview",
+ "mermaid": {
+ "summary": "Mermaid Diagram"
+ }
},
"bufferEditor": {
"title": "Buffer editor",
@@ -1212,5 +1221,10 @@
"node": "Node",
"junction": "Junction",
"linkNodes": "Link Nodes"
+ },
+ "env-var": {
+ "environment": "Environment",
+ "header": "Global Environment Variables",
+ "revert": "Revert"
}
}
diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/infotips.json b/packages/node_modules/@node-red/editor-client/locales/en-US/infotips.json
old mode 100755
new mode 100644
diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/jsonata.json b/packages/node_modules/@node-red/editor-client/locales/en-US/jsonata.json
old mode 100755
new mode 100644
diff --git a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json
index fb3458eed..6e78b195f 100644
--- a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json
+++ b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json
@@ -53,8 +53,10 @@
"confirmDelete": "削除の確認",
"delete": "本当に '__label__' を削除しますか?",
"dropFlowHere": "ここにフローをドロップしてください",
+ "dropImageHere": "ここに画像ファイルをドロップしてください",
"addFlow": "フローの追加",
"addFlowToRight": "右側にフローを追加",
+ "closeFlow": "フローを閉じる",
"hideFlow": "フローを非表示",
"hideOtherFlows": "他のフローを非表示",
"showAllFlows": "全てのフローを表示",
@@ -68,7 +70,11 @@
"enabled": "有効",
"disabled": "無効",
"info": "詳細",
- "selectNodes": "ノードをクリックして選択"
+ "selectNodes": "ノードをクリックして選択",
+ "enableFlow": "フローを有効化",
+ "disableFlow": "フローを無効化",
+ "moveToStart": "フローを先頭へ移動",
+ "moveToEnd": "フローを最後へ移動"
},
"menu": {
"label": {
@@ -683,7 +689,9 @@
"empty": "空",
"globalConfig": "グローバル設定ノード",
"triggerAction": "アクションを実行",
- "find": "ワークスペース内を検索"
+ "find": "ワークスペース内を検索",
+ "copyItemUrl": "要素のURLをコピー",
+ "copyURL2Clipboard": "URLをクリップボードにコピーしました"
},
"help": {
"name": "ヘルプ",
@@ -935,8 +943,11 @@
"errors": {
"invalid-expr": "不正なJSONata式:\n __message__",
"invalid-msg": "不正なJSONメッセージ例:\n __message__",
- "context-unsupported": "$flowContext や $globalContextの\nコンテキスト機能をテストできません",
- "eval": "表現評価エラー:\n __message__"
+ "context-unsupported": "$flowContext や $globalContextの\nコンテキスト関数をテストできません",
+ "env-unsupported": "$env関数はテストできません",
+ "moment-unsupported": "$moment関数はテストできません",
+ "clone-unsupported": "$clone関数はテストできません",
+ "eval": "式評価エラー:\n __message__"
}
},
"monaco": {
@@ -981,7 +992,10 @@
"quote": "引用",
"link": "リンク",
"horizontal-rule": "区切り線",
- "toggle-preview": "プレビュー表示切替え"
+ "toggle-preview": "プレビュー表示切替え",
+ "mermaid": {
+ "summary": "Mermaid図"
+ }
},
"bufferEditor": {
"title": "バッファエディタ",
@@ -1168,8 +1182,7 @@
"takeATour": "ツアーを開始",
"start": "開始",
"next": "次へ",
- "welcomeTours": "ウェルカムツアー",
- "tours": "ツアー"
+ "welcomeTours": "ウェルカムツアー"
},
"diagnostics": {
"title": "システム情報"
@@ -1348,6 +1361,15 @@
"show-project-settings": "プロジェクト設定を表示",
"show-version-control-tab": "バージョンコントロールタブを表示",
"start-flows": "フローを開始",
- "stop-flows": "フローを停止"
+ "stop-flows": "フローを停止",
+ "copy-item-url": "要素のURLをコピー",
+ "copy-item-edit-url": "要素の編集URLをコピー",
+ "move-flow-to-start": "フローを先頭に移動",
+ "move-flow-to-end": "フローを末尾に移動"
+ },
+ "env-var": {
+ "environment": "環境変数",
+ "header": "大域環境変数",
+ "revert": "破棄"
}
}
diff --git a/packages/node_modules/@node-red/editor-client/locales/ko/editor.json b/packages/node_modules/@node-red/editor-client/locales/ko/editor.json
old mode 100755
new mode 100644
diff --git a/packages/node_modules/@node-red/editor-client/locales/ko/infotips.json b/packages/node_modules/@node-red/editor-client/locales/ko/infotips.json
old mode 100755
new mode 100644
diff --git a/packages/node_modules/@node-red/editor-client/locales/ko/jsonata.json b/packages/node_modules/@node-red/editor-client/locales/ko/jsonata.json
old mode 100755
new mode 100644
diff --git a/packages/node_modules/@node-red/editor-client/locales/ru/editor.json b/packages/node_modules/@node-red/editor-client/locales/ru/editor.json
old mode 100755
new mode 100644
diff --git a/packages/node_modules/@node-red/editor-client/locales/ru/infotips.json b/packages/node_modules/@node-red/editor-client/locales/ru/infotips.json
old mode 100755
new mode 100644
diff --git a/packages/node_modules/@node-red/editor-client/locales/ru/jsonata.json b/packages/node_modules/@node-red/editor-client/locales/ru/jsonata.json
old mode 100755
new mode 100644
diff --git a/packages/node_modules/@node-red/editor-client/package.json b/packages/node_modules/@node-red/editor-client/package.json
index dbcdb1ba5..2fcd5950b 100644
--- a/packages/node_modules/@node-red/editor-client/package.json
+++ b/packages/node_modules/@node-red/editor-client/package.json
@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-client",
- "version": "3.1.0-beta.0",
+ "version": "3.1.0-beta.1",
"license": "Apache-2.0",
"repository": {
"type": "git",
diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js
index a7bfc1191..36bc2a7fa 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js
@@ -902,14 +902,7 @@ RED.nodes = (function() {
var node;
if (allNodes.hasTab(id)) {
- removedNodes = allNodes.getNodes(id).filter(n => {
- if (n.type === 'junction') {
- removedJunctions.push(n)
- return false
- } else {
- return true
- }
- })
+ removedNodes = allNodes.getNodes(id).slice()
}
for (i in configNodes) {
if (configNodes.hasOwnProperty(i)) {
@@ -919,6 +912,7 @@ RED.nodes = (function() {
}
}
}
+ removedJunctions = RED.nodes.junctions(id)
for (i=0;i 0) {
var reimportList = [];
@@ -2824,6 +2823,12 @@ RED.nodes = (function() {
} else {
allNodes.removeNode(n);
}
+ if (n.g) {
+ // reimporting a node *without* including its group object
+ // will cause the g property to be cleared. Cache it
+ // here so we can restore it
+ nodeGroupMap[n.id] = n.g
+ }
reimportList.push(convertNode(n));
RED.events.emit('nodes:remove',n);
});
@@ -2845,6 +2850,18 @@ RED.nodes = (function() {
var newNodeMap = {};
result.nodes.forEach(function(n) {
newNodeMap[n.id] = n;
+ if (nodeGroupMap[n.id]) {
+ // This node is in a group - need to substitute the
+ // node reference inside the group
+ n.g = nodeGroupMap[n.id]
+ const group = RED.nodes.group(n.g)
+ if (group) {
+ var index = group.nodes.findIndex(gn => gn.id === n.id)
+ if (index > -1) {
+ group.nodes[index] = n
+ }
+ }
+ }
});
RED.nodes.eachLink(function(l) {
if (newNodeMap.hasOwnProperty(l.source.id)) {
diff --git a/packages/node_modules/@node-red/editor-client/src/js/red.js b/packages/node_modules/@node-red/editor-client/src/js/red.js
index 114066d91..ce3b439e4 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/red.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/red.js
@@ -348,6 +348,8 @@ var RED = (function() {
loader.end()
RED.notify($("").text(message));
RED.sidebar.info.refresh()
+ RED.menu.setDisabled('menu-item-projects-open',false);
+ RED.menu.setDisabled('menu-item-projects-settings',false);
});
});
return;
@@ -775,6 +777,7 @@ var RED = (function() {
RED.deploy.init(RED.settings.theme("deployButton",null));
RED.keyboard.init(buildMainMenu);
+ RED.envVar.init();
RED.nodes.init();
RED.runtime.init()
@@ -793,7 +796,7 @@ var RED = (function() {
$('
').appendTo(header);
$(''+
'
'+
- '
'+
+ '
'+
'
'+
''+
''+
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js b/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js
index a8bcf9c4a..f24205be1 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js
@@ -656,7 +656,12 @@ RED.clipboard = (function() {
$("#red-ui-clipboard-dialog-tab-library-name").val("flows.json").select();
dialogContainer.i18n();
+
var format = RED.settings.flowFilePretty ? "red-ui-clipboard-dialog-export-fmt-full" : "red-ui-clipboard-dialog-export-fmt-mini";
+ const userFormat = RED.settings.get("editor.dialog.export.pretty")
+ if (userFormat === false || userFormat === true) {
+ format = userFormat ? "red-ui-clipboard-dialog-export-fmt-full" : "red-ui-clipboard-dialog-export-fmt-mini";
+ }
$("#red-ui-clipboard-dialog-export-fmt-group > a").on("click", function(evt) {
evt.preventDefault();
@@ -672,7 +677,8 @@ RED.clipboard = (function() {
var nodes = JSON.parse(flow);
format = $(this).attr('id');
- if (format === 'red-ui-clipboard-dialog-export-fmt-full') {
+ const pretty = format === "red-ui-clipboard-dialog-export-fmt-full";
+ if (pretty) {
flow = JSON.stringify(nodes,null,4);
} else {
flow = JSON.stringify(nodes);
@@ -681,6 +687,7 @@ RED.clipboard = (function() {
setTimeout(function() { $("#red-ui-clipboard-dialog-export-text").scrollTop(0); },50);
$("#red-ui-clipboard-dialog-export-text").trigger("focus");
+ RED.settings.set("editor.dialog.export.pretty", pretty)
}
});
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js
index ea1938e5c..f308614d7 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js
@@ -160,7 +160,7 @@
this.element.css("maxHeight",null);
}
if (this.options.height !== 'auto') {
- this.uiContainer.css("overflow-y","scroll");
+ this.uiContainer.css("overflow-y","auto");
if (!isNaN(this.options.height)) {
this.uiHeight = this.options.height;
}
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js
index a8e7ea727..4aa6ac232 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js
@@ -829,7 +829,7 @@ RED.tabs = (function() {
event.preventDefault();
removeTab(tab.id);
});
- RED.popover.tooltip(closeLink,RED._("workspace.hideFlow"));
+ RED.popover.tooltip(closeLink,RED._("workspace.closeFlow"));
}
// if (tab.hideable) {
// li.addClass("red-ui-tabs-closeable")
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js
index 549479d85..7440b464e 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js
@@ -90,10 +90,10 @@
optEl.append(generateSpans(srcMatch));
optEl.appendTo(element);
}
- matches.push({
- value: optVal,
- label: element,
- i: (valMatch.found ? valMatch.index : srcMatch.index)
+ matches.push({
+ value: optVal,
+ label: element,
+ i: (valMatch.found ? valMatch.index : srcMatch.index)
});
}
})
@@ -146,7 +146,7 @@
{ value: "reset", source: ["delay","trigger","join","rbe"] },
{ value: "responseCookies", source: ["http request"] },
{ value: "responseTopic", source: ["mqtt"] },
- { value: "responseURL", source: ["http request"] },
+ { value: "responseUrl", source: ["http request"] },
{ value: "restartTimeout", source: ["join"] },
{ value: "retain", source: ["mqtt"] },
{ value: "schema", source: ["json"] },
@@ -501,7 +501,7 @@
this.options.types = this.options.types||Object.keys(allOptions);
}
- this.selectTrigger = $('
').prependTo(this.uiSelect);
+ this.selectTrigger = $('
').prependTo(this.uiSelect);
$('
').toggle(this.options.types.length > 1).appendTo(this.selectTrigger);
this.selectLabel = $('
').appendTo(this.selectTrigger);
@@ -570,7 +570,7 @@
})
// explicitly set optionSelectTrigger display to inline-block otherwise jQ sets it to 'inline'
- this.optionSelectTrigger = $('
').appendTo(this.uiSelect);
+ this.optionSelectTrigger = $('
').appendTo(this.uiSelect);
this.optionSelectLabel = $('
').prependTo(this.optionSelectTrigger);
// RED.popover.tooltip(this.optionSelectLabel,function() {
// return that.optionValue;
@@ -591,7 +591,7 @@
that.uiSelect.addClass('red-ui-typedInput-focus');
});
- this.optionExpandButton = $('
').appendTo(this.uiSelect);
+ this.optionExpandButton = $('
').appendTo(this.uiSelect);
this.optionExpandButtonIcon = $('
').appendTo(this.optionExpandButton);
this.type(this.typeField.val() || this.options.default||this.typeList[0].value);
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js
index 29202a0ab..ec02c6281 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js
@@ -238,6 +238,7 @@ RED.editor = (function() {
var valid = validateNodeProperty(node, defaults, property,value);
if (((typeof valid) === "string") || !valid) {
input.addClass("input-error");
+ input.next(".red-ui-typedInput-container").addClass("input-error");
if ((typeof valid) === "string") {
var tooltip = input.data("tooltip");
if (tooltip) {
@@ -250,6 +251,7 @@ RED.editor = (function() {
}
} else {
input.removeClass("input-error");
+ input.next(".red-ui-typedInput-container").removeClass("input-error");
var tooltip = input.data("tooltip");
if (tooltip) {
input.data("tooltip", null);
@@ -1105,6 +1107,10 @@ RED.editor = (function() {
if (editing_node) {
RED.sidebar.info.refresh(editing_node);
RED.sidebar.help.show(editing_node.type, false);
+ //ensure focused element is NOT body (for keyboard scope to operate correctly)
+ if (document.activeElement.tagName === 'BODY') {
+ $('#red-ui-editor-stack').trigger('focus')
+ }
}
}
}
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editor.js
index 7cee2026b..b92881764 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editor.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editor.js
@@ -45,6 +45,9 @@
selectedCodeEditor = RED.editor.codeEditor[defaultEditor];
initialised = selectedCodeEditor.init();
}
+
+ $('
').appendTo('#red-ui-editor');
+ $("#red-ui-image-drop-target").hide();
}
function create(options) {
@@ -64,6 +67,7 @@
options = {};
}
+ var editor = null;
if (this.editor.type === MONACO) {
// compatibility (see above note)
if (!options.element && !options.id) {
@@ -74,10 +78,14 @@
console.warn("createEditor() options.element or options.id is not valid", options);
$("#dialog-form").append('
');
}
- return this.editor.create(options);
+ editor = this.editor.create(options);
} else {
- return this.editor.create(options);//fallback to ACE
+ editor = this.editor.create(options);//fallback to ACE
}
+ if (options.mode === "ace/mode/markdown") {
+ RED.editor.customEditTypes['_markdown'].postInit(editor, options);
+ }
+ return editor;
}
return {
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/monaco.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/monaco.js
index 701e3da44..68b9a487e 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/monaco.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/monaco.js
@@ -100,7 +100,7 @@ RED.editor.codeEditor.monaco = (function() {
"node-red-util": {package: "node-red", module: "util", path: "node-red/util.d.ts" },
"node-red-func": {package: "node-red", module: "func", path: "node-red/func.d.ts" },
}
- const defaultServerSideTypes = [ knownModules["node-red-util"], knownModules["node-red-func"], knownModules["globals"], knownModules["console"], knownModules["buffer"] ];
+ const defaultServerSideTypes = [ knownModules["node-red-util"], knownModules["node-red-func"], knownModules["globals"], knownModules["console"], knownModules["buffer"] , knownModules["util"] ];
const modulesCache = {};
@@ -764,7 +764,7 @@ RED.editor.codeEditor.monaco = (function() {
if(!options.stateId && options.stateId !== false) {
- options.stateId = RED.editor.generateViewStateId("monaco", options, (options.mode || options.title).split("/").pop());
+ options.stateId = RED.editor.generateViewStateId("monaco", options, (options.mode || options.title || "").split("/").pop());
}
var el = options.element || $("#"+options.id)[0];
var toolbarRow = $("
").appendTo(el);
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/colorPicker.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/colorPicker.js
index 4b2e19e5c..5b76d020b 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/colorPicker.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/colorPicker.js
@@ -76,6 +76,9 @@ RED.editor.colorPicker = RED.colorPicker = (function() {
var focusTarget = colorInput;
colorInput.on("change", function (e) {
var color = colorInput.val();
+ if (options.defaultValue && !color.match(/^([a-z]+|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3})$/)) {
+ color = options.defaultValue;
+ }
colorHiddenInput.val(color).trigger('change');
refreshDisplay(color);
});
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/expression.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/expression.js
index b3c4c3848..d470e14f2 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/expression.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/expression.js
@@ -255,6 +255,9 @@
var currentExpression = expressionEditor.getValue();
var expr;
var usesContext = false;
+ var usesEnv = false;
+ var usesMoment = false;
+ var usesClone = false;
var legacyMode = /(^|[^a-zA-Z0-9_'".])msg([^a-zA-Z0-9_'"]|$)/.test(currentExpression);
$(".red-ui-editor-type-expression-legacy").toggle(legacyMode);
try {
@@ -267,6 +270,18 @@
usesContext = true;
return null;
});
+ expr.assign("env", function(name) {
+ usesEnv = true;
+ return null;
+ });
+ expr.assign("moment", function(name) {
+ usesMoment = true;
+ return null;
+ });
+ expr.assign("clone", function(name) {
+ usesClone = true;
+ return null;
+ });
} catch(err) {
testResultEditor.setValue(RED._("expressionEditor.errors.invalid-expr",{message:err.message}),-1);
return;
@@ -284,6 +299,18 @@
testResultEditor.setValue(RED._("expressionEditor.errors.context-unsupported"),-1);
return;
}
+ if (usesEnv) {
+ testResultEditor.setValue(RED._("expressionEditor.errors.env-unsupported"),-1);
+ return;
+ }
+ if (usesMoment) {
+ testResultEditor.setValue(RED._("expressionEditor.errors.moment-unsupported"),-1);
+ return;
+ }
+ if (usesClone) {
+ testResultEditor.setValue(RED._("expressionEditor.errors.clone-unsupported"),-1);
+ return;
+ }
var formattedResult;
if (result !== undefined) {
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/markdown.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/markdown.js
index eeb8519e6..bd7a11b3f 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/markdown.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/markdown.js
@@ -14,6 +14,61 @@
* limitations under the License.
**/
(function() {
+ /**
+ * Converts dropped image file to date URL
+ */
+ function file2base64Image(file, cb) {
+ var reader = new FileReader();
+ reader.onload = (function (fd) {
+ return function (e) {
+ cb(e.target.result);
+ };
+ })(file);
+ reader.readAsDataURL(file);
+ }
+
+ var initialized = false;
+ var currentEditor = null;
+ /**
+ * Initialize handler for image file drag events
+ */
+ function initImageDrag(elem, editor) {
+ $(elem).on("dragenter", function (ev) {
+ ev.preventDefault();
+ $("#red-ui-image-drop-target").css({display:'table'}).focus();
+ currentEditor = editor;
+ });
+
+ if (!initialized) {
+ initialized = true;
+ $("#red-ui-image-drop-target").on("dragover", function (ev) {
+ ev.preventDefault();
+ }).on("dragleave", function (ev) {
+ $("#red-ui-image-drop-target").hide();
+ }).on("drop", function (ev) {
+ ev.preventDefault();
+ if ($.inArray("Files",ev.originalEvent.dataTransfer.types) != -1) {
+ var files = ev.originalEvent.dataTransfer.files;
+ if (files.length === 1) {
+ var file = files[0];
+ var name = file.name.toLowerCase();
+
+ if (name.match(/\.(apng|avif|gif|jpeg|png|svg|webp)$/)) {
+ file2base64Image(file, function (image) {
+ var session = currentEditor.getSession();
+ var img = `
\n`;
+ var pos = session.getCursorPosition();
+ session.insert(pos, img);
+ $("#red-ui-image-drop-target").hide();
+ });
+ return;
+ }
+ }
+ }
+ $("#red-ui-image-drop-target").hide();
+ });
+ }
+ }
var toolbarTemplate = '
'+
'
'+
@@ -114,6 +169,7 @@
var currentScrollTop = $(".red-ui-editor-type-markdown-panel-preview").scrollTop();
$(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue()));
$(".red-ui-editor-type-markdown-panel-preview").scrollTop(currentScrollTop);
+ mermaid.init();
},200);
})
if (options.header) {
@@ -122,6 +178,7 @@
if (value) {
$(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue()));
+ mermaid.init();
}
panels = RED.panels.create({
id:"red-ui-editor-type-markdown-panels",
@@ -148,10 +205,14 @@
});
RED.popover.tooltip($("#node-btn-markdown-preview"), RED._("markdownEditor.toggle-preview"));
- if (options.cursor && !expressionEditor._initState) {
- expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
- }
-
+ if(!expressionEditor._initState) {
+ if (options.cursor) {
+ expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
+ }
+ else {
+ expressionEditor.gotoLine(0, 0, false);
+ }
+ }
dialogForm.i18n();
},
close: function() {
@@ -215,7 +276,11 @@
}
})
return toolbar;
- }
+ },
+ postInit: function (editor, options) {
+ var elem = $("#"+options.id);
+ initImageDrag(elem, editor);
+ }
}
RED.editor.registerTypeEditor("_markdown", definition);
})();
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/appearance.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/appearance.js
index 912fa3528..e1e6b2ba3 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/appearance.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/appearance.js
@@ -235,6 +235,7 @@
RED.editor.colorPicker.create({
id: "red-ui-editor-node-color",
value: color,
+ defaultValue: "#DDAA99",
palette: recommendedColors,
sortPalette: function (a, b) {return a.l - b.l;}
}).appendTo(colorRow);
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js b/packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js
new file mode 100644
index 000000000..ab071a1d4
--- /dev/null
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js
@@ -0,0 +1,175 @@
+RED.envVar = (function() {
+ function saveEnvList(list) {
+ const items = list.editableList("items")
+ const new_env = [];
+ items.each(function (i,el) {
+ var data = el.data('data');
+ var item;
+ if (data.nameField && data.valueField) {
+ item = {
+ name: data.nameField.val(),
+ value: data.valueField.typedInput("value"),
+ type: data.valueField.typedInput("type")
+ };
+ new_env.push(item);
+ }
+ });
+ return new_env;
+ }
+
+ function getGlobalConf(create) {
+ var gconf = null;
+ RED.nodes.eachConfig(function (conf) {
+ if (conf.type === "global-config") {
+ gconf = conf;
+ }
+ });
+ if ((gconf === null) && create) {
+ var cred = {
+ _ : {},
+ map: {}
+ };
+ gconf = {
+ id: RED.nodes.id(),
+ type: "global-config",
+ env: [],
+ name: "global-config",
+ label: "",
+ hasUsers: false,
+ users: [],
+ credentials: cred,
+ _def: RED.nodes.getType("global-config"),
+ };
+ RED.nodes.add(gconf);
+ }
+ return gconf;
+ }
+
+ function applyChanges(list) {
+ var gconf = getGlobalConf(false);
+ var new_env = [];
+ var items = list.editableList('items');
+ var credentials = gconf ? gconf.credentials : null;
+
+ if (!credentials) {
+ credentials = {
+ _ : {},
+ map: {}
+ };
+ }
+ items.each(function (i,el) {
+ var data = el.data('data');
+ if (data.nameField && data.valueField) {
+ var item = {
+ name: data.nameField.val(),
+ value: data.valueField.typedInput("value"),
+ type: data.valueField.typedInput("type")
+ };
+ if (item.name.trim() !== "") {
+ new_env.push(item);
+ if ((item.type === "cred") && (item.value !== "__PWRD__")) {
+ credentials.map[item.name] = item.value;
+ credentials.map["has_"+item.name] = (item.value !== "");
+ item.value = "__PWRD__";
+ }
+ }
+ }
+ });
+ if (gconf === null) {
+ gconf = getGlobalConf(true);
+ }
+ if ((JSON.stringify(new_env) !== JSON.stringify(gconf.env)) ||
+ (JSON.stringify(credentials) !== JSON.stringify(gconf.credentials))) {
+ gconf.env = new_env;
+ gconf.credentials = credentials;
+ RED.nodes.dirty(true);
+ }
+ }
+
+ function getSettingsPane() {
+ var gconf = getGlobalConf(false);
+ var env = gconf ? gconf.env : [];
+ var cred = gconf ? gconf.credentials : null;
+ if (!cred) {
+ cred = {
+ _ : {},
+ map: {}
+ };
+ }
+
+ var pane = $("
", {
+ id: "red-ui-settings-tab-envvar",
+ class: "form-horizontal"
+ });
+ var content = $("
", {
+ class: "form-row node-input-env-container-row"
+ }).css({
+ "margin": "10px"
+ }).appendTo(pane);
+
+ var label = $(" ").css({
+ width: "100%"
+ }).appendTo(content);
+ $(" ", {
+ class: "fa fa-list"
+ }).appendTo(label);
+ $(" ").text(" "+RED._("env-var.header")).appendTo(label);
+
+ var list = $(" ", {
+ id: "node-input-env-container"
+ }).appendTo(content);
+ var node = {
+ type: "",
+ env: env,
+ credentials: cred.map,
+ };
+ RED.editor.envVarList.create(list, node);
+
+ var buttons = $("
").css({
+ "text-align": "right",
+ }).appendTo(content);
+ var revertButton = $(" ", {
+ class: "red-ui-button"
+ }).css({
+ }).text(RED._("env-var.revert")).appendTo(buttons);
+
+ var items = saveEnvList(list);
+ revertButton.on("click", function (ev) {
+ list.editableList("empty");
+ list.editableList("addItems", items);
+ });
+
+ return pane;
+ }
+
+ function init(done) {
+ if (!RED.user.hasPermission("settings.write")) {
+ RED.notify(RED._("user.errors.settings"),"error");
+ return;
+ }
+ RED.userSettings.add({
+ id:'envvar',
+ title: RED._("env-var.environment"),
+ get: getSettingsPane,
+ focus: function() {
+ var height = $("#red-ui-settings-tab-envvar").parent().height();
+ $("#node-input-env-container").editableList("height", (height -100));
+ },
+ close: function() {
+ var list = $("#node-input-env-container");
+ try {
+ applyChanges(list);
+ }
+ catch (e) {
+ console.log(e);
+ console.log(e.stack);
+ }
+ }
+ });
+ }
+
+ return {
+ init: init,
+ };
+
+})();
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/group.js b/packages/node_modules/@node-red/editor-client/src/js/ui/group.js
index 15eda31d5..399e5e36e 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/group.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/group.js
@@ -101,6 +101,7 @@ RED.group = (function() {
RED.editor.colorPicker.create({
id:"node-input-style-stroke",
value: style.stroke || defaultGroupStyle.stroke || "#a4a4a4",
+ defaultValue: "#a4a4a4",
palette: colorPalette,
cellPerRow: colorCount,
cellWidth: 16,
@@ -112,6 +113,7 @@ RED.group = (function() {
RED.editor.colorPicker.create({
id:"node-input-style-fill",
value: style.fill || defaultGroupStyle.fill ||"none",
+ defaultValue: "none",
palette: colorPalette,
cellPerRow: colorCount,
cellWidth: 16,
@@ -129,6 +131,7 @@ RED.group = (function() {
RED.editor.colorPicker.create({
id:"node-input-style-color",
value: style.color || defaultGroupStyle.color ||"#a4a4a4",
+ defaultValue: "#a4a4a4",
palette: colorPalette,
cellPerRow: colorCount,
cellWidth: 16,
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/library.js b/packages/node_modules/@node-red/editor-client/src/js/ui/library.js
old mode 100755
new mode 100644
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/mermaid.js b/packages/node_modules/@node-red/editor-client/src/js/ui/mermaid.js
new file mode 100644
index 000000000..d126cf188
--- /dev/null
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/mermaid.js
@@ -0,0 +1,46 @@
+// Mermaid diagram stub library for on-demand dynamic loading
+// Will be overwritten after script loading by $.getScript
+var mermaid = (function () {
+ var enabled /* = undefined */;
+
+ var initializing = false;
+ var initCalled = false;
+
+ function initialize(opt) {
+ if (enabled === undefined) {
+ if (RED.settings.markdownEditor &&
+ RED.settings.markdownEditor.mermaid) {
+ enabled = RED.settings.markdownEditor.mermaid.enabled;
+ }
+ else {
+ enabled = true;
+ }
+ }
+ if (enabled) {
+ initializing = true;
+ $.getScript("vendor/mermaid/mermaid.min.js",
+ function (data, stat, jqxhr) {
+ $(".mermaid").show();
+ // invoke loaded mermaid API
+ initializing = false;
+ mermaid.initialize(opt);
+ if (initCalled) {
+ mermaid.init();
+ initCalled = false;
+ }
+ });
+ }
+ }
+
+ function init() {
+ if (initializing) {
+ $(".mermaid").hide();
+ initCalled = true;
+ }
+ }
+
+ return {
+ initialize: initialize,
+ init: init,
+ };
+})();
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js b/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js
old mode 100755
new mode 100644
index 11ec82772..8e444b5ca
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js
@@ -175,9 +175,19 @@ RED.palette = (function() {
$(' ').appendTo(popOverContent)
}
- var safeType = type.replace(/'/g,"\\'");
+ const safeType = type.replace(/'/g,"\\'");
+ const wrapStr = function (str) {
+ if(str.indexOf(' ') >= 0) {
+ return '"' + str + '"'
+ }
+ return str
+ }
- $(' ').appendTo(popOverContent)
+ $(' ')
+ .appendTo(popOverContent)
+ .on('click', function() {
+ RED.search.show('type:' + wrapStr(safeType))
+ })
$(' ').appendTo(popOverContent)
$('',{style:"font-size: 0.8em"}).text(metaData).appendTo(popOverContent);
@@ -428,6 +438,7 @@ RED.palette = (function() {
categoryNode.find(".red-ui-palette-content").slideToggle();
categoryNode.find("i").toggleClass("expanded");
}
+ categoryNode.hide();
}
}
@@ -506,6 +517,7 @@ RED.palette = (function() {
currentCategoryNode.find(".red-ui-palette-content").slideToggle();
currentCategoryNode.find("i").toggleClass("expanded");
}
+ currentCategoryNode.hide();
}
}
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js b/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js
old mode 100755
new mode 100644
index 190561e15..c198b34db
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js
@@ -545,7 +545,7 @@ RED.projects = (function() {
var sshwarningRow = $('
').hide().appendTo(subrow);
$(' '+RED._("projects.clone-project.ssh-key-desc")+'
').appendTo(sshwarningRow);
subrow = $('').appendTo(sshwarningRow);
- $('
'+RED._("projects.clone-project.ssh-key-add")+' ').appendTo(subrow).on("click", function(e) {
+ $('
'+RED._("projects.clone-project.ssh-key-add")+' ').appendTo(subrow).on("click", function(e) {
e.preventDefault();
dialog.dialog( "close" );
RED.userSettings.show('gitconfig');
@@ -1171,11 +1171,11 @@ RED.projects = (function() {
row = $('
').appendTo(container);
- var openProject = $('
'+RED._("projects.create.open")+'').appendTo(row);
- var createAsEmpty = $('
'+RED._("projects.create.create")+'').appendTo(row);
- // var createAsCopy = $('
Copy existing').appendTo(row);
- var createAsClone = $('
'+RED._("projects.create.clone")+'').appendTo(row);
- // var createAsClone = $('
Clone Repository').appendTo(row);
+ var openProject = $('
'+RED._("projects.create.open")+'').appendTo(row);
+ var createAsEmpty = $('
'+RED._("projects.create.create")+'').appendTo(row);
+ // var createAsCopy = $('
Copy existing').appendTo(row);
+ var createAsClone = $('
'+RED._("projects.create.clone")+'').appendTo(row);
+ // var createAsClone = $('
Clone Repository').appendTo(row);
row.find(".red-ui-projects-dialog-screen-create-type").on("click", function(evt) {
evt.preventDefault();
container.find(".red-ui-projects-dialog-screen-create-type").removeClass('selected');
@@ -1271,7 +1271,7 @@ RED.projects = (function() {
var credentialsLeftBox = $('
').appendTo(credentialsBox);
var credentialsEnabledBox = $('
').appendTo(credentialsLeftBox);
- $('
'+RED._("projects.encryption-config.enable")+' ').appendTo(credentialsEnabledBox);
+ $('
'+RED._("projects.encryption-config.enable")+' ').appendTo(credentialsEnabledBox);
var credentialsDisabledBox = $('
').appendTo(credentialsLeftBox);
$('
'+RED._("projects.encryption-config.disable")+' ').appendTo(credentialsDisabledBox);
@@ -1397,7 +1397,7 @@ RED.projects = (function() {
var sshwarningRow = $('
').hide().appendTo(subrow);
$('
'+RED._("projects.create.desc2")+'
').appendTo(sshwarningRow);
subrow = $('
').appendTo(sshwarningRow);
- $('
'+RED._("projects.create.add-ssh-key")+' ').appendTo(subrow).on("click", function(e) {
+ $('
'+RED._("projects.create.add-ssh-key")+' ').appendTo(subrow).on("click", function(e) {
e.preventDefault();
$('#red-ui-projects-dialog-cancel').trigger("click");
RED.userSettings.show('gitconfig');
@@ -1631,14 +1631,14 @@ RED.projects = (function() {
function deleteProject(row,name,done) {
var cover = $('
').on("click", function(evt) { evt.stopPropagation(); }).appendTo(row);
$('
').text(RED._("projects.delete.confirm")).appendTo(cover);
- $(''+RED._("common.label.cancel")+' ')
+ $(''+RED._("common.label.cancel")+' ')
.appendTo(cover)
.on("click", function(e) {
e.stopPropagation();
cover.remove();
done(true);
});
- $(''+RED._("common.label.delete")+' ')
+ $(''+RED._("common.label.delete")+' ')
.appendTo(cover)
.on("click", function(e) {
e.stopPropagation();
@@ -1822,7 +1822,7 @@ RED.projects = (function() {
header.addClass("selectable");
var tools = $('
').appendTo(header);
- $(' ')
+ $(' ')
.appendTo(tools)
.on("click", function(e) {
e.stopPropagation();
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/search.js b/packages/node_modules/@node-red/editor-client/src/js/ui/search.js
index 217fb5a4e..3903a4a0a 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/search.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/search.js
@@ -106,38 +106,51 @@ RED.search = (function() {
return val;
}
- function search(val) {
- var results = [];
- var typeFilter;
- var m = /(?:^| )type:([^ ]+)/.exec(val);
- if (m) {
- val = val.replace(/(?:^| )type:[^ ]+/,"");
- typeFilter = m[1];
+ function extractType(val, flags) {
+ // extracts: type:XYZ & type:"X Y Z"
+ const regEx = /(?:type):\s*(?:"([^"]+)"|([^" ]+))/;
+ let m
+ while ((m = regEx.exec(val)) !== null) {
+ // avoid infinite loops with zero-width matches
+ if (m.index === regEx.lastIndex) {
+ regEx.lastIndex++;
+ }
+ val = val.replace(m[0]," ").trim()
+ const flag = m[2] || m[1] // quoted entries in capture group 1, unquoted in capture group 2
+ flags.type = flags.type || [];
+ flags.type.push(flag);
}
- var flags = {};
+ return val;
+ }
+
+ function search(val) {
+ const results = [];
+ const flags = {};
val = extractFlag(val,"invalid",flags);
val = extractFlag(val,"unused",flags);
val = extractFlag(val,"config",flags);
val = extractFlag(val,"subflow",flags);
val = extractFlag(val,"hidden",flags);
val = extractFlag(val,"modified",flags);
- val = extractValue(val,"flow",flags);// flow:active or flow:
+ val = extractValue(val,"flow",flags);// flow:current or flow:
val = extractValue(val,"uses",flags);// uses:
+ val = extractType(val,flags);// type:
val = val.trim();
- var hasFlags = Object.keys(flags).length > 0;
+ const hasFlags = Object.keys(flags).length > 0;
+ const hasTypeFilter = flags.type && flags.type.length > 0
if (flags.flow && flags.flow.indexOf("current") >= 0) {
let idx = flags.flow.indexOf("current");
- flags.flow[idx] = RED.workspaces.active();//convert active to flow ID
+ flags.flow[idx] = RED.workspaces.active();//convert 'current' to active flow ID
}
if (flags.flow && flags.flow.length) {
flags.flow = [ ...new Set(flags.flow) ]; //deduplicate
}
- if (val.length > 0 || typeFilter || hasFlags) {
+ if (val.length > 0 || hasFlags) {
val = val.toLowerCase();
- var i;
- var j;
- var list = [];
- var nodes = {};
+ let i;
+ let j;
+ let list = [];
+ const nodes = {};
let keys = [];
if (flags.uses) {
keys = flags.uses;
@@ -145,10 +158,10 @@ RED.search = (function() {
keys = Object.keys(index);
}
for (i=0;i -1) {
- var ids = Object.keys(index[key]||{});
+ const key = keys[i];
+ const kpos = val ? keys[i].indexOf(val) : -1;
+ if (kpos > -1 || (val === "" && hasFlags)) {
+ const ids = Object.keys(index[key]||{});
for (j=0;j -1) {
+ nodes[node.node.id] = nodes[node.node.id] || {
node: node.node,
label: node.label
};
- nodes[node.node.id].index = Math.min(nodes[node.node.id].index||Infinity,kpos);
+ nodes[node.node.id].index = Math.min(nodes[node.node.id].index || Infinity, typeIndex > -1 ? typeIndex : kpos);
}
}
}
@@ -538,7 +555,7 @@ RED.search = (function() {
$(previousActiveElement).trigger("focus");
}
previousActiveElement = null;
- }
+ }
if(!keepSearchToolbar) {
clearActiveSearch();
}
@@ -630,7 +647,7 @@ RED.search = (function() {
$("#red-ui-sidebar-shade").on('mousedown',hide);
$("#red-ui-view-searchtools-close").on("click", function close() {
- clearActiveSearch();
+ clearActiveSearch();
updateSearchToolbar();
});
$("#red-ui-view-searchtools-close").trigger("click");
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js
index 106e1a72a..b798ba891 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js
@@ -521,6 +521,13 @@ RED.subflow = (function() {
RED.nodes.groups(id).forEach(function(n) {
removedGroups.push(n);
})
+
+ var removedJunctions = RED.nodes.junctions(id)
+ for (var i=0;i", {class: "red-ui-sidebar-help-toc"}).appendTo(stackContainer);
var helpPanel = $("").css({
- "overflow-y": "scroll"
+ "overflow-y": "auto"
}).appendTo(stackContainer);
panels = RED.panels.create({
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js
index c46aa97e8..e38b0b67e 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js
@@ -108,7 +108,7 @@ RED.sidebar.info = (function() {
propertiesPanelContent = $("
").css({
"flex":"1 1 auto",
- "overflow-y":"scroll",
+ "overflow-y":"auto",
}).appendTo(propertiesPanel);
@@ -463,7 +463,8 @@ RED.sidebar.info = (function() {
el = el.next();
}
$(this).toggleClass('expanded',!isExpanded);
- })
+ });
+ mermaid.init();
}
var tips = (function() {
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js b/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js
index fc5b8e99e..989cb78ab 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js
@@ -269,8 +269,8 @@ RED.typeSearch = (function() {
moveCallback = opts.move;
RED.events.emit("type-search:open");
//shade.show();
- if ($("#red-ui-main-container").height() - opts.y - 150 < 0) {
- opts.y = opts.y - 235;
+ if ($("#red-ui-main-container").height() - opts.y - 195 < 0) {
+ opts.y = opts.y - 275;
}
dialog.css({left:opts.x+"px",top:opts.y+"px"}).show();
searchResultsDiv.slideDown(300);
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js
index 2c4cdca6b..4d8ccdd9d 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/utils.js
@@ -96,6 +96,37 @@ RED.utils = (function() {
}
}
+ var mermaidIsInitialized = false;
+ var mermaidIsEnabled /* = undefined */;
+
+ renderer.code = function (code, lang) {
+ if(lang === "mermaid") {
+ // mermaid diagram rendering
+ if (mermaidIsEnabled === undefined) {
+ if (RED.settings.markdownEditor &&
+ RED.settings.markdownEditor.mermaid) {
+ mermaidIsEnabled = RED.settings.markdownEditor.mermaid.enabled;
+ }
+ else {
+ mermaidIsEnabled = true;
+ }
+ }
+ if (mermaidIsEnabled) {
+ if (!mermaidIsInitialized) {
+ mermaidIsInitialized = true;
+ mermaid.initialize({startOnLoad:false});
+ }
+ return `
${code} `;
+ }
+ else {
+ return `
${RED._("markdownEditor.mermaid.summary")} ${code}
`;
+ }
+ }
+ else {
+ return "
" +code +"
";
+ }
+ };
+
window._marked.setOptions({
renderer: renderer,
gfm: true,
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js
index 27d45dcd3..f46167b24 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js
@@ -1043,7 +1043,7 @@ RED.view.tools = (function() {
const nodeDef = n._def || RED.nodes.getType(n.type)
if (nodeDef && nodeDef.defaults && nodeDef.defaults.name) {
const paletteLabel = RED.utils.getPaletteLabel(n.type, nodeDef)
- const defaultNodeNameRE = new RegExp('^'+paletteLabel+' (\\d+)$')
+ const defaultNodeNameRE = new RegExp('^'+paletteLabel.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')+' (\\d+)$')
if (!typeIndex.hasOwnProperty(n.type)) {
const existingNodes = RED.nodes.filterNodes({type: n.type})
let maxNameNumber = 0;
@@ -1242,7 +1242,7 @@ RED.view.tools = (function() {
url += '/edit'
}
if (RED.clipboard.copyText(url)) {
- RED.notify('Copied url to clipboard', { timeout: 2000 })
+ RED.notify(RED._("sidebar.info.copyURL2Clipboard"), { timeout: 2000 })
}
}
}
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js
old mode 100755
new mode 100644
index 1520471fd..488f7c474
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js
@@ -1105,12 +1105,15 @@ RED.view = (function() {
RED.view.redraw();
}
+ // `point` is the place in the workspace the mouse has clicked.
+ // This takes into account scrolling and scaling of the workspace.
var ox = point[0];
var oy = point[1];
+ // Need to map that to browser location to position the pop-up
const offset = $("#red-ui-workspace-chart").offset()
- var clientX = ox + offset.left - $("#red-ui-workspace-chart").scrollLeft()
- var clientY = oy + offset.top - $("#red-ui-workspace-chart").scrollTop()
+ var clientX = (ox * scaleFactor) + offset.left - $("#red-ui-workspace-chart").scrollLeft()
+ var clientY = (oy * scaleFactor) + offset.top - $("#red-ui-workspace-chart").scrollTop()
if (RED.settings.get("editor").view['view-snap-grid']) {
// eventLayer.append("circle").attr("cx",point[0]).attr("cy",point[1]).attr("r","2").attr('fill','red')
@@ -3413,6 +3416,9 @@ RED.view = (function() {
d3.event.stopPropagation();
return
}
+ // Avoid dbl click causing text selection.
+ d3.event.preventDefault()
+ document.getSelection().removeAllRanges()
if (d.type != "subflow") {
if (/^subflow:/.test(d.type) && (d3.event.ctrlKey || d3.event.metaKey)) {
RED.workspaces.show(d.type.substring(8));
@@ -4954,7 +4960,7 @@ RED.view = (function() {
if (d._def.button) {
var buttonEnabled = isButtonEnabled(d);
this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-disabled", !buttonEnabled);
- if (RED.runtime && Object.hasOwn(RED.runtime,'started')) {
+ if (RED.runtime && RED.runtime.started !== undefined) {
this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-stopped", !RED.runtime.started);
}
@@ -5651,7 +5657,24 @@ RED.view = (function() {
if (activeSubflow) {
activeSubflowChanged = activeSubflow.changed;
}
- var result = RED.nodes.import(nodesToImport,{generateIds:options.generateIds, addFlow: addNewFlow, importMap: options.importMap});
+ var filteredNodesToImport = nodesToImport;
+ var globalConfig = null;
+ var gconf = null;
+
+ RED.nodes.eachConfig(function (conf) {
+ if (conf.type === "global-config") {
+ gconf = conf;
+ }
+ });
+ if (gconf) {
+ filteredNodesToImport = nodesToImport.filter(function (n) {
+ return (n.type !== "global-config");
+ });
+ globalConfig = nodesToImport.find(function (n) {
+ return (n.type === "global-config");
+ });
+ }
+ var result = RED.nodes.import(filteredNodesToImport,{generateIds:options.generateIds, addFlow: addNewFlow, importMap: options.importMap});
if (result) {
var new_nodes = result.nodes;
var new_links = result.links;
@@ -5783,6 +5806,50 @@ RED.view = (function() {
}
}
+ if (globalConfig) {
+ // merge global env to existing global-config
+ var env0 = gconf.env;
+ var env1 = globalConfig.env;
+ var newEnv = Array.from(env0);
+ var changed = false;
+
+ env1.forEach(function (item1) {
+ var index = newEnv.findIndex(function (item0) {
+ return (item0.name === item1.name);
+ });
+ if (index >= 0) {
+ var item0 = newEnv[index];
+ if ((item0.type !== item1.type) ||
+ (item0.value !== item1.value)) {
+ newEnv[index] = item1;
+ changed = true;
+ }
+ }
+ else {
+ newEnv.push(item1);
+ changed = true;
+ }
+ });
+ if(changed) {
+ gconf.env = newEnv;
+ var replaceEvent = {
+ t: "edit",
+ node: gconf,
+ changed: true,
+ changes: {
+ env: env0
+ }
+ };
+ historyEvent = {
+ t:"multi",
+ events: [
+ replaceEvent,
+ historyEvent,
+ ]
+ };
+ }
+ }
+
RED.history.push(historyEvent);
updateActiveNodes();
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js
index f127b7662..788e79455 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js
@@ -152,6 +152,13 @@ RED.workspaces = (function() {
const hiddenflowCount = hiddenFlows.size;
let activeWorkspace = tab || RED.nodes.workspace(RED.workspaces.active()) || RED.nodes.subflow(RED.workspaces.active())
let isFlowDisabled = activeWorkspace ? activeWorkspace.disabled : false
+ const currentTabs = workspace_tabs.listTabs();
+ let flowCount = 0;
+ currentTabs.forEach(tab => {
+ if (RED.nodes.workspace(tab)) {
+ flowCount++;
+ }
+ });
let isCurrentLocked = RED.workspaces.isActiveLocked()
if (tab) {
@@ -224,8 +231,7 @@ RED.workspaces = (function() {
null
)
}
- const currentTabs = workspace_tabs.listTabs()
- const activeIndex = currentTabs.findIndex(id => id === activeWorkspace.id)
+ const activeIndex = currentTabs.findIndex(id => (activeWorkspace && (id === activeWorkspace.id)));
menuItems.push(
{
label: RED._("workspace.moveToStart"),
@@ -267,11 +273,13 @@ RED.workspaces = (function() {
)
}
+
menuItems.push(
{
id:"red-ui-tabs-menu-option-add-hide-all-flows",
label: RED._("workspace.hideAllFlows"),
- onselect: "core:hide-all-flows"
+ onselect: "core:hide-all-flows",
+ disabled: (hiddenflowCount === flowCount)
},
{
id:"red-ui-tabs-menu-option-add-show-all-flows",
@@ -298,7 +306,8 @@ RED.workspaces = (function() {
} else if (tab.type === 'subflow') {
RED.subflow.delete(tab.id)
}
- }
+ },
+ disabled: (workspaceTabCount === 1)
},
{
label: RED._("menu.label.export"),
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/debug.scss b/packages/node_modules/@node-red/editor-client/src/sass/debug.scss
index 58099877f..eb550c6f5 100644
--- a/packages/node_modules/@node-red/editor-client/src/sass/debug.scss
+++ b/packages/node_modules/@node-red/editor-client/src/sass/debug.scss
@@ -30,7 +30,7 @@
bottom: 0px;
left:0px;
right: 0px;
- overflow-y: scroll;
+ overflow-y: auto;
}
.red-ui-debug-filter-box {
position:absolute;
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/dragdrop.scss b/packages/node_modules/@node-red/editor-client/src/sass/dragdrop.scss
index 78646e0e7..3d4b2253a 100644
--- a/packages/node_modules/@node-red/editor-client/src/sass/dragdrop.scss
+++ b/packages/node_modules/@node-red/editor-client/src/sass/dragdrop.scss
@@ -37,3 +37,27 @@
}
}
}
+
+#red-ui-image-drop-target {
+ position: absolute;
+ top: 0; bottom: 0;
+ left: 0; right: 0;
+ background: var(--red-ui-dnd-background);
+ display:table;
+ width: 100%;
+ height: 100%;
+ display: none;
+ z-index:100;
+ div {
+ pointer-events: none;
+ display: table-cell;
+ vertical-align: middle;
+ text-align: center;
+ font-size: 40px;
+ color: var(--red-ui-dnd-color);
+ i {
+ pointer-events: none;
+ font-size: 80px;
+ }
+ }
+}
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/editor.scss b/packages/node_modules/@node-red/editor-client/src/sass/editor.scss
index 1730b9e35..b6d12faf2 100644
--- a/packages/node_modules/@node-red/editor-client/src/sass/editor.scss
+++ b/packages/node_modules/@node-red/editor-client/src/sass/editor.scss
@@ -368,7 +368,7 @@ button.red-ui-button-small
border:1px solid var(--red-ui-secondary-border-color);
border-radius:5px;
height: calc(100% - 21px);
- overflow-y: scroll;
+ overflow-y: auto;
background: var(--red-ui-secondary-background);
}
@@ -562,7 +562,7 @@ div.red-ui-button-small.red-ui-color-picker-opacity-slider-handle {
.red-ui-icon-list {
width: 308px;
height: 200px;
- overflow-y: scroll;
+ overflow-y: auto;
line-height: 0px;
position: relative;
&.red-ui-icon-list-dark {
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/notifications.scss b/packages/node_modules/@node-red/editor-client/src/sass/notifications.scss
index efae432b2..7d7544e2e 100644
--- a/packages/node_modules/@node-red/editor-client/src/sass/notifications.scss
+++ b/packages/node_modules/@node-red/editor-client/src/sass/notifications.scss
@@ -32,7 +32,8 @@
color: var(--red-ui-primary-text-color);
border: 1px solid var(--red-ui-notification-border-default);
border-left-width: 16px;
- overflow: hidden;
+ overflow: scroll;
+ max-height: 80vh;
.ui-dialog-buttonset {
margin-top: 20px;
margin-bottom: 10px;
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/projects.scss b/packages/node_modules/@node-red/editor-client/src/sass/projects.scss
index ee43c7a87..a32fd53b4 100644
--- a/packages/node_modules/@node-red/editor-client/src/sass/projects.scss
+++ b/packages/node_modules/@node-red/editor-client/src/sass/projects.scss
@@ -26,7 +26,7 @@
}
}
#red-ui-project-settings-tab-settings {
- overflow-y: scroll;
+ overflow-y: auto;
}
.red-ui-sidebar-vc-shade {
background: var(--red-ui-primary-background);
@@ -183,7 +183,7 @@
}
.red-ui-projects-dialog-project-list-inner-container {
flex-grow: 1 ;
- overflow-y: scroll;
+ overflow-y: auto;
position:relative;
.red-ui-editableList-border {
border: none;
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/radialMenu.scss b/packages/node_modules/@node-red/editor-client/src/sass/radialMenu.scss
index 3348e945a..db1bba3bb 100644
--- a/packages/node_modules/@node-red/editor-client/src/sass/radialMenu.scss
+++ b/packages/node_modules/@node-red/editor-client/src/sass/radialMenu.scss
@@ -41,6 +41,7 @@
height: 50px;
background: var(--red-ui-secondary-background);
border: 2px solid var(--red-ui-primary-border-color);
+ color: var(--red-ui-primary-text-color);
text-align: center;
line-height:50px;
@@ -51,7 +52,7 @@
.red-ui-editor-radial-menu-opt-disabled {
border-color: var(--red-ui-tertiary-border-color);
- color: var(--red-ui-tertiary-border-color);
+ color: var(--red-ui-secondary-text-color-disabled);
}
.red-ui-editor-radial-menu-opt-active {
background: var(--red-ui-secondary-background-hover);
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/tab-context.scss b/packages/node_modules/@node-red/editor-client/src/sass/tab-context.scss
index fc4c78afb..94450f337 100644
--- a/packages/node_modules/@node-red/editor-client/src/sass/tab-context.scss
+++ b/packages/node_modules/@node-red/editor-client/src/sass/tab-context.scss
@@ -20,7 +20,7 @@
bottom: 0;
left: 0;
right: 0;
- overflow-y: scroll;
+ overflow-y: auto;
.red-ui-palette-category {
&:not(.expanded) button {
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/userSettings.scss b/packages/node_modules/@node-red/editor-client/src/sass/userSettings.scss
index 36ab67e3f..5e0c7fa47 100644
--- a/packages/node_modules/@node-red/editor-client/src/sass/userSettings.scss
+++ b/packages/node_modules/@node-red/editor-client/src/sass/userSettings.scss
@@ -67,7 +67,7 @@
left: 0;
bottom: 0;
padding: 8px 20px 20px;
- overflow-y: scroll;
+ overflow-y: auto;
}
.red-ui-settings-row {
padding: 5px 10px 2px;
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/workspace.scss b/packages/node_modules/@node-red/editor-client/src/sass/workspace.scss
index c458d03d1..06ec5d4fc 100644
--- a/packages/node_modules/@node-red/editor-client/src/sass/workspace.scss
+++ b/packages/node_modules/@node-red/editor-client/src/sass/workspace.scss
@@ -29,7 +29,7 @@
#red-ui-workspace-chart {
overflow: auto;
position: absolute;
- bottom:25px;
+ bottom:26px;
top: 35px;
left:0px;
right:0px;
diff --git a/packages/node_modules/@node-red/editor-client/src/types/node-red/func.d.ts b/packages/node_modules/@node-red/editor-client/src/types/node-red/func.d.ts
index ae411f33c..fd2adcbd8 100644
--- a/packages/node_modules/@node-red/editor-client/src/types/node-red/func.d.ts
+++ b/packages/node_modules/@node-red/editor-client/src/types/node-red/func.d.ts
@@ -14,6 +14,9 @@ declare var msg: NodeMessage;
/** @type {string} the id of the incoming `msg` (alias of msg._msgid) */
declare const __msgid__:string;
+declare const util:typeof import('util')
+declare const promisify:typeof import('util').promisify
+
/**
* @typedef NodeStatus
* @type {object}
diff --git a/packages/node_modules/@node-red/editor-client/src/vendor/jsonata/formatter.js b/packages/node_modules/@node-red/editor-client/src/vendor/jsonata/formatter.js
index 3d1af605c..2431a8bbd 100644
--- a/packages/node_modules/@node-red/editor-client/src/vendor/jsonata/formatter.js
+++ b/packages/node_modules/@node-red/editor-client/src/vendor/jsonata/formatter.js
@@ -160,6 +160,7 @@
'$base64encode':{ args:[ ]},
'$boolean':{ args:[ 'arg' ]},
'$ceil':{ args:[ 'number' ]},
+ '$clone': { args:[ 'arg' ]},
'$contains':{ args:[ 'str', 'pattern' ]},
'$count':{ args:[ 'array' ]},
'$decodeUrl':{ args:[ 'str' ]},
diff --git a/packages/node_modules/@node-red/nodes/core/common/20-inject.js b/packages/node_modules/@node-red/nodes/core/common/20-inject.js
index 734bce765..3f2992cd5 100644
--- a/packages/node_modules/@node-red/nodes/core/common/20-inject.js
+++ b/packages/node_modules/@node-red/nodes/core/common/20-inject.js
@@ -95,45 +95,64 @@ module.exports = function(RED) {
}
this.on("input", function(msg, send, done) {
- var errors = [];
- var props = this.props;
+ const errors = [];
+ let props = this.props;
if (msg.__user_inject_props__ && Array.isArray(msg.__user_inject_props__)) {
props = msg.__user_inject_props__;
}
delete msg.__user_inject_props__;
- props.forEach(p => {
- var property = p.p;
- var value = p.v ? p.v : '';
- var valueType = p.vt ? p.vt : 'str';
+ props = [...props]
+ function evaluateProperty(doneEvaluating) {
+ if (props.length === 0) {
+ doneEvaluating()
+ return
+ }
+ const p = props.shift()
+ const property = p.p;
+ const value = p.v ? p.v : '';
+ const valueType = p.vt ? p.vt : 'str';
- if (!property) return;
-
- if (valueType === "jsonata") {
- if (p.v) {
- try {
- var exp = RED.util.prepareJSONataExpression(p.v, node);
- var val = RED.util.evaluateJSONataExpression(exp, msg);
- RED.util.setMessageProperty(msg, property, val, true);
+ if (property) {
+ if (valueType === "jsonata") {
+ if (p.v) {
+ try {
+ var exp = RED.util.prepareJSONataExpression(p.v, node);
+ var val = RED.util.evaluateJSONataExpression(exp, msg);
+ RED.util.setMessageProperty(msg, property, val, true);
+ }
+ catch (err) {
+ errors.push(err.message);
+ }
}
- catch (err) {
- errors.push(err.message);
+ evaluateProperty(doneEvaluating)
+ } else {
+ try {
+ RED.util.evaluateNodeProperty(value, valueType, node, msg, (err, newValue) => {
+ if (err) {
+ errors.push(err.toString())
+ } else {
+ RED.util.setMessageProperty(msg,property,newValue,true);
+ }
+ evaluateProperty(doneEvaluating)
+ })
+ } catch (err) {
+ errors.push(err.toString());
+ evaluateProperty(doneEvaluating)
}
}
- return;
+ } else {
+ evaluateProperty(doneEvaluating)
}
- try {
- RED.util.setMessageProperty(msg,property,RED.util.evaluateNodeProperty(value, valueType, this, msg),true);
- } catch (err) {
- errors.push(err.toString());
- }
- });
-
- if (errors.length) {
- done(errors.join('; '));
- } else {
- send(msg);
- done();
}
+
+ evaluateProperty(() => {
+ if (errors.length) {
+ done(errors.join('; '));
+ } else {
+ send(msg);
+ done();
+ }
+ })
});
}
diff --git a/packages/node_modules/@node-red/nodes/core/common/24-complete.html b/packages/node_modules/@node-red/nodes/core/common/24-complete.html
index a6a7a2a45..b2d406bb5 100644
--- a/packages/node_modules/@node-red/nodes/core/common/24-complete.html
+++ b/packages/node_modules/@node-red/nodes/core/common/24-complete.html
@@ -1,6 +1,6 @@
+
+
diff --git a/packages/node_modules/@node-red/nodes/core/common/91-global-config.js b/packages/node_modules/@node-red/nodes/core/common/91-global-config.js
new file mode 100644
index 000000000..08aa23207
--- /dev/null
+++ b/packages/node_modules/@node-red/nodes/core/common/91-global-config.js
@@ -0,0 +1,7 @@
+module.exports = function(RED) {
+ "use strict";
+ function GlobalConfigNode(n) {
+ RED.nodes.createNode(this,n);
+ }
+ RED.nodes.registerType("global-config", GlobalConfigNode);
+}
diff --git a/packages/node_modules/@node-red/nodes/core/function/10-function.html b/packages/node_modules/@node-red/nodes/core/function/10-function.html
index 48e46da13..e17f58aca 100644
--- a/packages/node_modules/@node-red/nodes/core/function/10-function.html
+++ b/packages/node_modules/@node-red/nodes/core/function/10-function.html
@@ -17,6 +17,8 @@
display: flex;
background: var(--red-ui-tertiary-background);
padding-right: 75px;
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
}
#node-input-libs-container-row .red-ui-editableList-header > div {
flex-grow: 1;
@@ -91,21 +93,21 @@
@@ -294,7 +296,7 @@
if (val === "_custom_") {
val = $(this).val();
}
- var varName = val.trim().replace(/^@/,"").replace(/@.*$/,"").replace(/[-_/].?/g, function(v) { return v[1]?v[1].toUpperCase():"" });
+ var varName = val.trim().replace(/^@/,"").replace(/@.*$/,"").replace(/[-_/\.].?/g, function(v) { return v[1]?v[1].toUpperCase():"" });
fvar.val(varName);
fvar.trigger("change");
@@ -451,11 +453,13 @@
tabs.activateTab("func-tab-body");
$( "#node-input-outputs" ).spinner({
- min:0,
+ min: 0,
+ max: 500,
change: function(event, ui) {
- var value = this.value;
- if (!value.match(/^\d+$/)) { value = 1; }
- else if (value < this.min) { value = this.min; }
+ var value = parseInt(this.value);
+ value = isNaN(value) ? 1 : value;
+ value = Math.max(value, parseInt($(this).attr("aria-valuemin")));
+ value = Math.min(value, parseInt($(this).attr("aria-valuemax")));
if (value !== this.value) { $(this).spinner("value", value); }
}
});
diff --git a/packages/node_modules/@node-red/nodes/core/function/15-change.js b/packages/node_modules/@node-red/nodes/core/function/15-change.js
index 94f47fcbf..ee0e8d9c7 100644
--- a/packages/node_modules/@node-red/nodes/core/function/15-change.js
+++ b/packages/node_modules/@node-red/nodes/core/function/15-change.js
@@ -318,7 +318,7 @@ module.exports = function(RED) {
}
var r = node.rules[currentRule];
if (r.t === "move") {
- if ((r.tot !== r.pt) || (r.p.indexOf(r.to) !== -1)) {
+ if ((r.tot !== r.pt) || (r.p.indexOf(r.to) !== -1) && (r.p !== r.to)) {
applyRule(msg,{t:"set", p:r.to, pt:r.tot, to:r.p, tot:r.pt},(err,msg) => {
applyRule(msg,{t:"delete", p:r.p, pt:r.pt}, (err,msg) => {
completeApplyingRules(msg,currentRule,done);
diff --git a/packages/node_modules/@node-red/nodes/core/function/16-range.html b/packages/node_modules/@node-red/nodes/core/function/16-range.html
index 07bb1f080..1652a91db 100644
--- a/packages/node_modules/@node-red/nodes/core/function/16-range.html
+++ b/packages/node_modules/@node-red/nodes/core/function/16-range.html
@@ -10,6 +10,7 @@
+
diff --git a/packages/node_modules/@node-red/nodes/core/function/16-range.js b/packages/node_modules/@node-red/nodes/core/function/16-range.js
index a5dede4ea..61ffd53fb 100644
--- a/packages/node_modules/@node-red/nodes/core/function/16-range.js
+++ b/packages/node_modules/@node-red/nodes/core/function/16-range.js
@@ -32,11 +32,15 @@ module.exports = function(RED) {
if (value !== undefined) {
var n = Number(value);
if (!isNaN(n)) {
- if (node.action == "clamp") {
+ if (node.action === "drop") {
+ if (n < node.minin) { done(); return; }
+ if (n > node.maxin) { done(); return; }
+ }
+ if (node.action === "clamp") {
if (n < node.minin) { n = node.minin; }
if (n > node.maxin) { n = node.maxin; }
}
- if (node.action == "roll") {
+ if (node.action === "roll") {
var divisor = node.maxin - node.minin;
n = ((n - node.minin) % divisor + divisor) % divisor + node.minin;
}
diff --git a/packages/node_modules/@node-red/nodes/core/function/80-template.html b/packages/node_modules/@node-red/nodes/core/function/80-template.html
index 940487001..6f970a0cf 100644
--- a/packages/node_modules/@node-red/nodes/core/function/80-template.html
+++ b/packages/node_modules/@node-red/nodes/core/function/80-template.html
@@ -21,12 +21,13 @@
JavaScript
CSS
Markdown
+
PHP
Python
SQL
YAML
-
+
diff --git a/packages/node_modules/@node-red/nodes/core/function/89-trigger.html b/packages/node_modules/@node-red/nodes/core/function/89-trigger.html
index 572856c92..8d6083442 100644
--- a/packages/node_modules/@node-red/nodes/core/function/89-trigger.html
+++ b/packages/node_modules/@node-red/nodes/core/function/89-trigger.html
@@ -25,7 +25,7 @@
-
+
@@ -181,6 +181,13 @@
$("#node-input-op2type").val('str');
}
+ $("#node-input-op1").on("change", function() {
+ if ($("#node-input-op1type").val() === "nul") {
+ $("#node-trigger-wait-loop").hide();
+ }
+ else { $("#node-trigger-wait-loop").show(); }
+ });
+
var optionNothing = {value:"nul",label:this._("trigger.output.nothing"),hasValue:false};
var optionPayload = {value:"pay",label:this._("trigger.output.existing"),hasValue:false};
var optionOriginalPayload = {value:"pay",label:this._("trigger.output.original"),hasValue:false};
diff --git a/packages/node_modules/@node-red/nodes/core/network/05-tls.html b/packages/node_modules/@node-red/nodes/core/network/05-tls.html
index c25e41123..8414e98ca 100644
--- a/packages/node_modules/@node-red/nodes/core/network/05-tls.html
+++ b/packages/node_modules/@node-red/nodes/core/network/05-tls.html
@@ -25,7 +25,7 @@
-
+
@@ -37,7 +37,7 @@
-
+
@@ -53,7 +53,7 @@
-
+
diff --git a/packages/node_modules/@node-red/nodes/core/network/06-httpproxy.html b/packages/node_modules/@node-red/nodes/core/network/06-httpproxy.html
index 7819ab8ec..446cbd324 100644
--- a/packages/node_modules/@node-red/nodes/core/network/06-httpproxy.html
+++ b/packages/node_modules/@node-red/nodes/core/network/06-httpproxy.html
@@ -101,6 +101,7 @@
hostField.val(data.host);
}
},
+ sortable: true,
removable: true
});
if (this.noproxy) {
diff --git a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.html b/packages/node_modules/@node-red/nodes/core/network/10-mqtt.html
index 03ef4b2e3..bb76b33bc 100644
--- a/packages/node_modules/@node-red/nodes/core/network/10-mqtt.html
+++ b/packages/node_modules/@node-red/nodes/core/network/10-mqtt.html
@@ -421,7 +421,11 @@
diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/common/91-global-config.html b/packages/node_modules/@node-red/nodes/locales/en-US/common/91-global-config.html
new file mode 100644
index 000000000..0a3021762
--- /dev/null
+++ b/packages/node_modules/@node-red/nodes/locales/en-US/common/91-global-config.html
@@ -0,0 +1,3 @@
+
diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/function/16-range.html b/packages/node_modules/@node-red/nodes/locales/en-US/function/16-range.html
index b391f5c04..f25363565 100644
--- a/packages/node_modules/@node-red/nodes/locales/en-US/function/16-range.html
+++ b/packages/node_modules/@node-red/nodes/locales/en-US/function/16-range.html
@@ -34,11 +34,14 @@
the range specified within the target range.
Scale and wrap within the target range means that the result will
be wrapped within the target range.
+
Scale, but drop if outside input range means that the result will
+ be scaled, but any inputs outside of the inout range will be dropped.
For example an input 0 - 10 mapped to 0 - 100.
mode input output
scale 12 120
limit 12 100
wrap 12 20
+ drop 12 (no output)
diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/function/80-template.html b/packages/node_modules/@node-red/nodes/locales/en-US/function/80-template.html
index 07027e76e..52b6c2920 100644
--- a/packages/node_modules/@node-red/nodes/locales/en-US/function/80-template.html
+++ b/packages/node_modules/@node-red/nodes/locales/en-US/function/80-template.html
@@ -52,4 +52,7 @@
used to mark the templated sections. For example, to use
[[ ]]
instead, add the following line to the top of the template:
{{=[[ ]]=}}
+
Using environment variables
+
The template node can access environment variables using the syntax:
+
My favourite colour is {{env.COLOUR}}.
diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
index 62d5f351f..248bb3415 100644
--- a/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
+++ b/packages/node_modules/@node-red/nodes/locales/en-US/messages.json
@@ -446,7 +446,9 @@
"staticTopic": "Subscribe to single topic",
"dynamicTopic": "Dynamic subscription",
"auto-connect": "Connect automatically",
- "auto-mode-depreciated": "This option is depreciated. Please use the new auto-detect mode."
+ "auto-mode-depreciated": "This option is depreciated. Please use the new auto-detect mode.",
+ "none": "none",
+ "other": "other"
},
"sections-label": {
"birth-message": "Message sent on connection (birth message)",
@@ -554,7 +556,8 @@
},
"status": {
"requesting": "requesting"
- }
+ },
+ "insecureHTTPParser": "Disable strict HTTP parsing"
},
"websocket": {
"label": {
@@ -813,7 +816,8 @@
"scale": {
"payload": "Scale the message property",
"limit": "Scale and limit to the target range",
- "wrap": "Scale and wrap within the target range"
+ "wrap": "Scale and wrap within the target range",
+ "drop": "Scale, but drop msg if outside input range"
},
"tip": "Tip: This node ONLY works with numbers.",
"errors": {
@@ -1123,5 +1127,10 @@
"warn": {
"nonumber": "no number found in payload"
}
+ },
+ "global-config": {
+ "label": {
+ "open-conf": "Open Configuration"
+ }
}
}
diff --git a/packages/node_modules/@node-red/nodes/locales/en-US/parsers/70-CSV.html b/packages/node_modules/@node-red/nodes/locales/en-US/parsers/70-CSV.html
index dc4dd98ff..baa3b036b 100644
--- a/packages/node_modules/@node-red/nodes/locales/en-US/parsers/70-CSV.html
+++ b/packages/node_modules/@node-red/nodes/locales/en-US/parsers/70-CSV.html
@@ -47,5 +47,6 @@
If 'include null values' option is checked, null values will be returned in result, ie. middle value '"1",,3'.
The node can accept a multi-part input as long as the parts
property is set correctly, for example from a file-in node or split node.
If outputting multiple messages they will have their parts
property set and form a complete message sequence.
+
If the node is set to only send column headers once, then setting msg.reset
to any value will cause the node to resend the headers.
Note: the column template must be comma separated - even if a different separator is chosen for the data.
diff --git a/packages/node_modules/@node-red/nodes/locales/ja/common/91-global-config.html b/packages/node_modules/@node-red/nodes/locales/ja/common/91-global-config.html
new file mode 100644
index 000000000..2eda840ec
--- /dev/null
+++ b/packages/node_modules/@node-red/nodes/locales/ja/common/91-global-config.html
@@ -0,0 +1,3 @@
+p
diff --git a/packages/node_modules/@node-red/nodes/locales/ja/function/80-template.html b/packages/node_modules/@node-red/nodes/locales/ja/function/80-template.html
index 1a9c17453..9b64003a5 100644
--- a/packages/node_modules/@node-red/nodes/locales/ja/function/80-template.html
+++ b/packages/node_modules/@node-red/nodes/locales/ja/function/80-template.html
@@ -48,4 +48,7 @@
注: デフォルトでは、mustache 形式は置換対象のHTML要素をエスケープします。これを抑止するには{{{三重}}}
括弧形式を使います。
もし、コンテンツの中で{{ }}
を出力する必要がある場合は、テンプレートで使われる記号文字を変えることもできます。例えば、[[ ]]
を代わりに用いるには、テンプレートの先頭に以下の行を追加します。
{{=[[ ]]=}}
+
環境変数の利用
+
templateノードでは、次の構文を用いると環境変数にアクセスできます:
+
私の好きな色は{{env.COLOUR}}です。
diff --git a/packages/node_modules/@node-red/nodes/locales/ja/messages.json b/packages/node_modules/@node-red/nodes/locales/ja/messages.json
index 6e16daa6f..263fbce97 100644
--- a/packages/node_modules/@node-red/nodes/locales/ja/messages.json
+++ b/packages/node_modules/@node-red/nodes/locales/ja/messages.json
@@ -446,7 +446,9 @@
"staticTopic": "1つのトピックを購読",
"dynamicTopic": "動的な購読",
"auto-connect": "自動接続",
- "auto-mode-depreciated": "本オプションは非推奨になりました。新しい自動判定モードを使用してください。"
+ "auto-mode-depreciated": "本オプションは非推奨になりました。新しい自動判定モードを使用してください。",
+ "none": "なし",
+ "other": "その他"
},
"sections-label": {
"birth-message": "接続時の送信メッセージ(Birthメッセージ)",
@@ -554,7 +556,8 @@
},
"status": {
"requesting": "要求中"
- }
+ },
+ "insecureHTTPParser": "厳密なHTTPパース処理を無効化"
},
"websocket": {
"label": {
@@ -777,8 +780,8 @@
"change": "値の置換",
"delete": "値の削除",
"move": "値の移動",
- "toValue": "対象の値",
- "to": "対象の値",
+ "toValue": "代入する値",
+ "to": "移動先",
"search": "検索する文字列",
"replace": "置換後の文字列"
},
@@ -813,7 +816,8 @@
"scale": {
"payload": "msg.payloadの値を拡大/縮小",
"limit": "入力値の範囲外の値を最小値/最大値とし拡大/縮小",
- "wrap": "入力値の範囲外の値を範囲幅で割った余りとし拡大/縮小"
+ "wrap": "入力値の範囲外の値を範囲幅で割った余りとし拡大/縮小",
+ "drop": "値を拡大/縮小(入力範囲外の時はメッセージを削除)"
},
"tip": "注釈: 本ノードは、数値のみ扱うことができます。",
"errors": {
@@ -1123,5 +1127,10 @@
"warn": {
"nonumber": "ペイロードに数値が含まれていません"
}
+ },
+ "global-config": {
+ "label": {
+ "open-conf": "設定を開く"
+ }
}
}
diff --git a/packages/node_modules/@node-red/nodes/locales/ja/parsers/70-CSV.html b/packages/node_modules/@node-red/nodes/locales/ja/parsers/70-CSV.html
index a7f144907..60645e4cc 100644
--- a/packages/node_modules/@node-red/nodes/locales/ja/parsers/70-CSV.html
+++ b/packages/node_modules/@node-red/nodes/locales/ja/parsers/70-CSV.html
@@ -43,5 +43,6 @@
「null値を含む」オプションがチェックされている場合、null値が結果に返されます。つまり「"1",,3」の真ん中の値がnullになります。
file-inノードやsplitノードが出力するメッセージの様に、parts
プロパティが正しく設定されている場合、メッセージ列を入力として受け付けます。
CSVを複数のメッセージに変換して出力する場合、出力がメッセージ列となるようparts
プロパティを設定します。
+
ヘッダを一度だけ送信するよう設定している場合、任意の値を持つmsg.reset
を渡すと、再度ヘッダを含めて送信できるようになります。
注: カンマ以外の区切り文字を設定した場合であっても、「列名」はカンマ区切りとしてください。
diff --git a/packages/node_modules/@node-red/nodes/locales/ko/messages.json b/packages/node_modules/@node-red/nodes/locales/ko/messages.json
old mode 100755
new mode 100644
diff --git a/packages/node_modules/@node-red/nodes/locales/ru/messages.json b/packages/node_modules/@node-red/nodes/locales/ru/messages.json
old mode 100755
new mode 100644
diff --git a/packages/node_modules/@node-red/nodes/package.json b/packages/node_modules/@node-red/nodes/package.json
index 9db16efc0..a4a6d18b5 100644
--- a/packages/node_modules/@node-red/nodes/package.json
+++ b/packages/node_modules/@node-red/nodes/package.json
@@ -1,6 +1,6 @@
{
"name": "@node-red/nodes",
- "version": "3.1.0-beta.0",
+ "version": "3.1.0-beta.1",
"license": "Apache-2.0",
"repository": {
"type": "git",
@@ -15,22 +15,22 @@
}
],
"dependencies": {
- "acorn": "8.7.1",
+ "acorn": "8.8.1",
"acorn-walk": "8.2.0",
- "ajv": "8.11.0",
- "body-parser": "1.20.0",
+ "ajv": "8.11.2",
+ "body-parser": "1.20.1",
"cheerio": "1.0.0-rc.10",
"content-type": "1.0.4",
"cookie-parser": "1.4.6",
"cookie": "0.5.0",
"cors": "2.8.5",
"cronosjs": "1.7.1",
- "denque": "2.0.1",
+ "denque": "2.1.0",
"form-data": "4.0.0",
"fs-extra": "10.1.0",
"got": "11.8.5",
"hash-sum": "2.0.0",
- "hpagent": "1.0.0",
+ "hpagent": "1.2.0",
"https-proxy-agent": "5.0.1",
"is-utf8": "0.2.1",
"js-yaml": "4.1.0",
@@ -41,7 +41,7 @@
"node-watch": "0.7.3",
"on-headers": "1.0.2",
"raw-body": "2.5.1",
- "tough-cookie": "4.0.0",
+ "tough-cookie": "4.1.2",
"uuid": "8.3.2",
"ws": "7.5.6",
"xml2js": "0.4.23",
diff --git a/packages/node_modules/@node-red/registry/lib/loader.js b/packages/node_modules/@node-red/registry/lib/loader.js
index 61f28ab86..d125156ab 100644
--- a/packages/node_modules/@node-red/registry/lib/loader.js
+++ b/packages/node_modules/@node-red/registry/lib/loader.js
@@ -43,37 +43,40 @@ function load(disableNodePathScan) {
return loadModuleFiles(modules);
}
+function splitPath(p) {
+ return path.posix.normalize((p || '').replace(/\\/g, path.sep)).split(path.sep)
+}
function loadModuleTypeFiles(module, type) {
const things = module[type];
- var first = true;
- var promises = [];
- for (var thingName in things) {
+ let first = true;
+ const promises = [];
+ for (let thingName in things) {
/* istanbul ignore else */
if (things.hasOwnProperty(thingName)) {
if (module.name != "node-red" && first) {
// Check the module directory exists
first = false;
- var fn = things[thingName].file;
- var parts = fn.split("/");
- var i = parts.length-1;
- for (;i>=0;i--) {
- if (parts[i] == "node_modules") {
- break;
- }
+ let moduleFn = module.path
+ const fn = things[thingName].file
+ const parts = splitPath(fn)
+ const nmi = parts.indexOf('node_modules')
+ if(nmi > -1) {
+ moduleFn = parts.slice(0,nmi+2).join(path.sep);
+ }
+ if (!moduleFn) {
+ // shortcut - skip calling statSync on empty string
+ break; // Module not found, don't attempt to load its nodes
}
- var moduleFn = parts.slice(0,i+2).join("/");
-
try {
- var stat = fs.statSync(moduleFn);
+ const stat = fs.statSync(moduleFn);
} catch(err) {
- // Module not found, don't attempt to load its nodes
- break;
+ break; // Module not found, don't attempt to load its nodes
}
}
try {
- var promise;
+ let promise;
if (type === "nodes") {
promise = loadNodeConfig(things[thingName]);
} else if (type === "plugins") {
@@ -82,8 +85,7 @@ function loadModuleTypeFiles(module, type) {
promises.push(
promise.then(
(function() {
- var m = module.name;
- var n = thingName;
+ const n = thingName;
return function(nodeSet) {
things[n] = nodeSet;
return nodeSet;
@@ -93,7 +95,6 @@ function loadModuleTypeFiles(module, type) {
);
} catch(err) {
console.log(err)
- //
}
}
}
@@ -125,38 +126,24 @@ function loadModuleFiles(modules) {
}
var pluginList;
var nodeList;
-
return Promise.all(pluginPromises).then(function(results) {
pluginList = results.filter(r => !!r);
- // Initial plugin load has happened. Ensure modules that provide
- // plugins are in the registry now.
- for (var module in modules) {
- if (modules.hasOwnProperty(module)) {
- if (modules[module].plugins && Object.keys(modules[module].plugins).length > 0) {
- // Add the modules for plugins
- if (!modules[module].err) {
- registry.addModule(modules[module]);
- }
- }
- }
- }
- return loadNodeSetList(pluginList);
- }).then(function() {
- return Promise.all(nodePromises);
+ return Promise.all(nodePromises)
}).then(function(results) {
nodeList = results.filter(r => !!r);
// Initial node load has happened. Ensure remaining modules are in the registry
for (var module in modules) {
if (modules.hasOwnProperty(module)) {
- if (!modules[module].plugins || Object.keys(modules[module].plugins).length === 0) {
- if (!modules[module].err) {
- registry.addModule(modules[module]);
- }
+ if (!modules[module].err) {
+ registry.addModule(modules[module]);
}
}
}
+ }).then(function() {
+ return loadNodeSetList(pluginList);
+ }).then(function() {
return loadNodeSetList(nodeList);
- });
+ })
}
async function loadPluginTemplate(plugin) {
diff --git a/packages/node_modules/@node-red/registry/lib/localfilesystem.js b/packages/node_modules/@node-red/registry/lib/localfilesystem.js
index da4006ecc..0c231552f 100644
--- a/packages/node_modules/@node-red/registry/lib/localfilesystem.js
+++ b/packages/node_modules/@node-red/registry/lib/localfilesystem.js
@@ -106,8 +106,8 @@ function getLocalNodeFiles(dir, skipValidNodeRedModules) {
// when loading local files, if the path is a valid node-red module
// dont include it (will be picked up in scanTreeForNodesModules)
if(skipValidNodeRedModules && files.indexOf("package.json") >= 0) {
- const package = getPackageDetails(dir)
- if(package.isNodeRedModule) {
+ const packageDetails = getPackageDetails(dir)
+ if(packageDetails.isNodeRedModule) {
return {files: [], icons: []};
}
}
@@ -135,17 +135,17 @@ function getLocalNodeFiles(dir, skipValidNodeRedModules) {
return {files: result, icons: icons}
}
-function scanDirForNodesModules(dir,moduleName,package) {
+function scanDirForNodesModules(dir,moduleName,packageDetails) {
let results = [];
let scopeName;
let files
try {
let isNodeRedModule = false
- if(package) {
- dir = path.join(package.moduleDir,'..')
- files = [path.basename(package.moduleDir)]
- moduleName = (package.package ? package.package.name : null) || moduleName
- isNodeRedModule = package.isNodeRedModule
+ if(packageDetails) {
+ dir = path.join(packageDetails.moduleDir,'..')
+ files = [path.basename(packageDetails.moduleDir)]
+ moduleName = (packageDetails.package ? packageDetails.package.name : null) || moduleName
+ isNodeRedModule = packageDetails.isNodeRedModule
} else {
files = fs.readdirSync(dir);
if (moduleName) {
@@ -156,6 +156,16 @@ function scanDirForNodesModules(dir,moduleName,package) {
}
}
}
+
+ // if we have found a package.json, this IS a node_module, lets see if it is a node-red node
+ if (!isNodeRedModule && files.indexOf('package.json') > -1) {
+ packageDetails = getPackageDetails(dir) // get package details
+ if(packageDetails && packageDetails.isNodeRedModule) {
+ isNodeRedModule = true
+ files = ['package.json'] // shortcut the file scan
+ }
+ }
+
for (let i=0;i
{
+ if (n.type === "global-config") {
+ gconf = n;
+ }
+ });
+ return gconf;
+}
+
module.exports = {
init: init,
@@ -790,6 +808,9 @@ module.exports = {
get:getNode,
eachNode: eachNode,
+
+ getGlobalConfig: getGlobalConfig,
+
/**
* Gets the current flow configuration
*/
diff --git a/packages/node_modules/@node-red/runtime/lib/flows/util.js b/packages/node_modules/@node-red/runtime/lib/flows/util.js
index 0f3435b77..f697fc930 100644
--- a/packages/node_modules/@node-red/runtime/lib/flows/util.js
+++ b/packages/node_modules/@node-red/runtime/lib/flows/util.js
@@ -18,7 +18,9 @@ var redUtil = require("@node-red/util").util;
var Log = require("@node-red/util").log;
var subflowInstanceRE = /^subflow:(.+)$/;
var typeRegistry = require("@node-red/registry");
+const credentials = require("../nodes/credentials");
+let _runtime = null;
var envVarExcludes = {};
@@ -134,10 +136,12 @@ function createNode(flow,config) {
subflowInstanceConfig,
instanceConfig
);
+ // Register this subflow as an instance node of the parent flow.
+ // This allows nodes inside the subflow to get ahold of each other
+ // such as a node accessing its config node
+ flow.subflowInstanceNodes[config.id] = subflow
subflow.start();
return subflow.node;
-
- Log.error(Log._("nodes.flow.unknown-type", {type:type}));
}
} catch(err) {
Log.error(err);
@@ -263,15 +267,55 @@ function parseConfig(config) {
return flow;
}
+function getGlobalEnv(name) {
+ const nodes = _runtime.nodes;
+ if (!nodes) {
+ return null;
+ }
+ const gconf = nodes.getGlobalConfig();
+ const env = gconf ? gconf.env : null;
+
+ if (env) {
+ const cred = (gconf ? credentials.get(gconf.id) : null) || {
+ map: {}
+ };
+ const map = cred.map;
+
+ for (let i = 0; i < env.length; i++) {
+ const item = env[i];
+ if (item.name === name) {
+ if (item.type === "cred") {
+ return {
+ name: name,
+ value: map[name],
+ type: "cred"
+ };
+ }
+ return item;
+ }
+ }
+ }
+ return null;
+}
+
module.exports = {
init: function(runtime) {
+ _runtime = runtime;
envVarExcludes = {};
if (runtime.settings.hasOwnProperty('envVarExcludes') && Array.isArray(runtime.settings.envVarExcludes)) {
runtime.settings.envVarExcludes.forEach(v => envVarExcludes[v] = true);
}
},
getEnvVar: function(k) {
- return !envVarExcludes[k]?process.env[k]:undefined
+ if (!envVarExcludes[k]) {
+ const item = getGlobalEnv(k);
+ if (item) {
+ const val = redUtil.evaluateNodeProperty(item.value, item.type, null, null, null);
+ return val;
+ }
+ return process.env[k];
+ }
+ return undefined;
},
diffNodes: diffNodes,
mapEnvVarProperties: mapEnvVarProperties,
diff --git a/packages/node_modules/@node-red/runtime/lib/index.js b/packages/node_modules/@node-red/runtime/lib/index.js
index 88b3b8293..a886cd2ca 100644
--- a/packages/node_modules/@node-red/runtime/lib/index.js
+++ b/packages/node_modules/@node-red/runtime/lib/index.js
@@ -161,6 +161,8 @@ function start() {
for (i=0;i {
rs.close();
@@ -419,7 +421,10 @@ module.exports = {
});
},
initRepo: function(cwd) {
- return runGitCommand(["init"],cwd);
+ var args = ["init", "--initial-branch", "main"];
+ return runGitCommand(args, cwd).catch(function () {
+ return runGitCommand(["init"], cwd);
+ });
},
setUpstream: function(cwd,remoteBranch) {
var args = ["branch","--set-upstream-to",remoteBranch];
diff --git a/packages/node_modules/@node-red/runtime/locales/de/runtime.json b/packages/node_modules/@node-red/runtime/locales/de/runtime.json
old mode 100755
new mode 100644
index 6811ba86a..0249d638d
--- a/packages/node_modules/@node-red/runtime/locales/de/runtime.json
+++ b/packages/node_modules/@node-red/runtime/locales/de/runtime.json
@@ -1,6 +1,6 @@
{
"runtime": {
- "welcome": "Willkommen bei Node-RED!",
+ "welcome": "Willkommen bei Node-RED",
"version": "__component__ Version: __version__",
"unsupported_version": "Nicht unterstützte Version von __component__. Erforderlich: __requires__, jedoch gefunden: __version__",
"paths": {
@@ -8,7 +8,6 @@
"httpStatic": "HTTP-Statisch: __path__"
}
},
-
"server": {
"loading": "Paletten-Nodes werden geladen",
"palette-editor": {
@@ -34,17 +33,19 @@
"install-failed-not-found": "Das Modul $t(server.install.install-failed-long) wurde nicht gefunden",
"install-failed-name": "$t(server.install.install-failed-long). Ungültiger Modulname: __name__",
"install-failed-url": "$t(server.install.install-failed-long). Ungültige URL: __url__",
+ "post-install-error": "Fehler bei der Ausführung des 'postInstall'-Hooks:",
"upgrading": "Upgrade von Modul __name__ auf Version __version__ gestartet",
"upgraded": "Upgrade von Modul __name__ war erfolgreich. Neustart von Node-RED für die Verwendung der neuen Version erforderlich.",
"upgrade-failed-not-found": "Upgrade fehlgeschlagen. $t(server.install.install-failed-long). Version nicht gefunden.",
"uninstalling": "Das Modul __name__ wird deinstalliert",
"uninstall-failed": "Deinstallation fehlgeschlagen",
"uninstall-failed-long": "Die Deinstallation des Moduls __name__ ist fehlgeschlagen:",
- "uninstalled": "Das Modul __name__ ist deinstalliert"
+ "uninstalled": "Das Modul __name__ ist deinstalliert",
+ "old-ext-mod-dir-warning": "\n\n---------------------------------------------------------------------\nNode-RED 1.3 Verzeichnis externer Module erkannt:\n __oldDir__\nDieses Verzeichnis wird nicht mehr verwendet. Die externen Module werden\nin Ihrem Node-RED-Benutzerverzeichnis neu installiert:\n __newDir__\nLöschen Sie das alte externalModules-Verzeichnis, um diese Meldung abzustellen.\n---------------------------------------------------------------------\n"
},
"deprecatedOption": "Die Verwendung von __old__ ist abgekündigt. Stattdessen __new__ verwenden.",
"unable-to-listen": "Überwachen (listen) von __listenpath__ nicht möglich",
- "port-in-use": "FEHLER: Port wird verwendet",
+ "port-in-use": "Fehler: Port wird verwendet",
"uncaught-exception": "Nicht abgefangene Ausnahmebedingung:",
"admin-ui-disabled": "Administrator-Benutzeroberfläche deaktiv",
"now-running": "Server wird jetzt auf __listenpath__ ausgeführt",
@@ -55,11 +56,10 @@
"refresh-interval": "Erneuerung der https-Einstellungen erfolgt alle __interval__ Stunden",
"settings-refreshed": "https-Einstellungen wurden erneuert",
"refresh-failed": "Erneuerung der https-Einstellungen fehlgeschlagen: __message__",
- "nodejs-version": "httpsRefreshInterval erfordert Node.js 11 or later",
+ "nodejs-version": "httpsRefreshInterval erfordert Node.js 11 oder höher",
"function-required": "httpsRefreshInterval erfordert die https-Eigenschaft in Form einer Funktion"
}
},
-
"api": {
"flows": {
"error-save": "Fehler beim Speichern der Flows: __message__",
@@ -77,17 +77,16 @@
"error-enable": "Der Node konnte nicht aktiviert werden:"
}
},
-
"comms": {
"error": "Kommunikationskanal-Fehler: __message__",
"error-server": "Kommunikationsserver-Fehler: __message__",
"error-send": "Kommunikationsende-Fehler: __message__"
},
-
"settings": {
"user-not-available": "Einstellungen konnten nicht gespeichert werden: __message__",
"not-available": "Einstellungen nicht verfügbar",
- "property-read-only": "Die Eigenschaft '__prop__ 'ist schreibgeschützt"
+ "property-read-only": "Die Eigenschaft '__prop__' ist schreibgeschützt",
+ "readonly-mode": "Laufzeitumgebung im Nur-Lese-Modus. Änderungen werden nicht gespeichert."
},
"library": {
"unknownLibrary": "Unbekannte Bibliothek (Library): __library__",
@@ -98,10 +97,12 @@
},
"nodes": {
"credentials": {
- "error": "Fehler beim Laden der Berechtigungen: __message__",
- "error-saving": "Fehler beim Speichern der Berechtigungen: __message__",
- "not-registered": "Der Berechtigung-Typ '__type__' ist nicht registriert",
- "system-key-warning": "\n---------------------------------------------------------------------\nDie Datei mit den Flow-Berechtigungen wird mit einem vom System\ngenerierten Schlüssel verschlüsselt.\nWenn der vom System generierte Schlüssel aus irgendeinem Grund\nverloren geht, kann die Datei mit den Berechtigungen nicht\nwiederhergestellt werden. Sie muss dann gelöscht und die\nBerechtigungen müssen erneut eingestellt werden.\nEs sollte ein eigener Schlüssel mit Hilfe der Option\n'credentialSecret' in der Einstellungsdatei vorgegeben werden.\nNode-RED wird dann die Datei mit den Flow-Berechtigungen\nbei der nächsten Übernahme (deploy) einer Änderung erneut\nverschlüsseln.\n---------------------------------------------------------------------"
+ "error": "Fehler beim Laden der Credentials: __message__",
+ "error-saving": "Fehler beim Speichern der Credentials: __message__",
+ "not-registered": "Der Credentials-Typ '__type__' ist nicht registriert",
+ "system-key-warning": "\n---------------------------------------------------------------------\nDie Datei mit den Flow-Credentials wird mit einem vom System\ngenerierten Schlüssel verschlüsselt.\nWenn der vom System generierte Schlüssel aus irgendeinem Grund\nverloren geht, kann die Datei mit den Credentials nicht\nwiederhergestellt werden. Sie muss dann gelöscht und die\nCredentials müssen erneut eingestellt werden.\nEs sollte ein eigener Schlüssel mit Hilfe der Option\n'credentialSecret' in der Einstellungsdatei vorgegeben werden.\nNode-RED wird dann die Datei mit den Flow-Credentials\nbei der nächsten Übernahme (deploy) einer Änderung erneut\nverschlüsseln.\n---------------------------------------------------------------------",
+ "unencrypted": "Verwende unverschlüsselte Credentials",
+ "encryptedNotFound": "Verschlüsselte Credentials nicht gefunden"
},
"flows": {
"safe-mode": "Die Flows sind gestoppt im abgesicherten Modus. Übernahme (deploy) zum Starten.",
@@ -121,6 +122,7 @@
"stopped-flows": "Flows sind gestoppt",
"stopped": "gestoppt",
"stopping-error": "Fehler beim Stoppen des Nodes: __message__",
+ "updated-flows": "Flows aktualisiert",
"added-flow": "Flow wird hinzugefügt: __label__",
"updated-flow": "Aktualisierter Flow: __label__",
"removed-flow": "Entfernter Flow: __label__",
@@ -145,7 +147,6 @@
}
}
},
-
"storage": {
"index": {
"forbidden-flow-name": "Unzulässiger Flow-Name"
@@ -159,6 +160,7 @@
"restore": "Die '__type__'-Dateisicherung wird wiederhergestellt: __path__",
"restore-fail": "Die Wiederherstellung der '__type__'-Dateisicherung ist fehlgeschlagen: __message__",
"fsync-fail": "Die Übertragung der Datei __path__ auf das Laufwerk ist fehlgeschlagen: __message__",
+ "warn_name": "Name der Flows-Datei nicht festgelegt. Name wird unter Verwendung des Hostnamens generiert.",
"projects": {
"changing-project": "Aktives Projekt wird festgelegt: __project__",
"active-project": "Aktives Projekt: __project__",
@@ -174,7 +176,6 @@
}
}
},
-
"context": {
"log-store-init": "Kontextspeicher: __name__ [__info__]",
"error-loading-module": "Fehler beim Laden des Kontextspeichers: __message__",
@@ -189,5 +190,4 @@
"error-write": "Fehler beim Schreiben des Kontextes: __message__"
}
}
-
}
diff --git a/packages/node_modules/@node-red/runtime/locales/en-US/runtime.json b/packages/node_modules/@node-red/runtime/locales/en-US/runtime.json
index ecd010abb..46890c26c 100644
--- a/packages/node_modules/@node-red/runtime/locales/en-US/runtime.json
+++ b/packages/node_modules/@node-red/runtime/locales/en-US/runtime.json
@@ -20,6 +20,7 @@
"errors-help": "Run with -v for details",
"missing-modules": "Missing node modules:",
"node-version-mismatch": "Node module cannot be loaded on this version. Requires: __version__ ",
+ "set-has-no-types": "Set does not have any types. name: '__name__', module: '__module__', file: '__file__'",
"type-already-registered": "'__type__' already registered by module __module__",
"removing-modules": "Removing modules from config",
"added-types": "Added node types:",
@@ -134,7 +135,8 @@
"flow": {
"unknown-type": "Unknown type: __type__",
"missing-types": "missing types",
- "error-loop": "Message exceeded maximum number of catches"
+ "error-loop": "Message exceeded maximum number of catches",
+ "non-message-returned": "Node tried to send a message of type __type__"
},
"index": {
"unrecognised-id": "Unrecognised id: __id__",
diff --git a/packages/node_modules/@node-red/runtime/locales/ja/runtime.json b/packages/node_modules/@node-red/runtime/locales/ja/runtime.json
index bb5b0badc..8e4bceebe 100644
--- a/packages/node_modules/@node-red/runtime/locales/ja/runtime.json
+++ b/packages/node_modules/@node-red/runtime/locales/ja/runtime.json
@@ -20,6 +20,7 @@
"errors-help": "詳細は -v を指定して実行してください",
"missing-modules": "不足しているノードモジュール:",
"node-version-mismatch": "ノードモジュールはこのバージョンではロードできません。必要なバージョン: __version__ ",
+ "set-has-no-types": "セットに型がありません。 名前: '__name__', モジュール: '__module__', ファイル: '__file__'",
"type-already-registered": "'__type__' はモジュール __module__ で登録済みです",
"removing-modules": "設定からモジュールを削除します",
"added-types": "追加したノード:",
@@ -134,7 +135,8 @@
"flow": {
"unknown-type": "不明なノード: __type__",
"missing-types": "欠落したノード",
- "error-loop": "メッセージの例外補足回数が最大値を超えました"
+ "error-loop": "メッセージの例外補足回数が最大値を超えました",
+ "non-message-returned": "ノードが __type__ 型のメッセージの送信を試みました"
},
"index": {
"unrecognised-id": "不明なID: __id__",
diff --git a/packages/node_modules/@node-red/runtime/locales/ko/runtime.json b/packages/node_modules/@node-red/runtime/locales/ko/runtime.json
old mode 100755
new mode 100644
diff --git a/packages/node_modules/@node-red/runtime/package.json b/packages/node_modules/@node-red/runtime/package.json
index 14171f1d6..58e84ab2d 100644
--- a/packages/node_modules/@node-red/runtime/package.json
+++ b/packages/node_modules/@node-red/runtime/package.json
@@ -1,6 +1,6 @@
{
"name": "@node-red/runtime",
- "version": "3.1.0-beta.0",
+ "version": "3.1.0-beta.1",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,11 +16,11 @@
}
],
"dependencies": {
- "@node-red/registry": "3.1.0-beta.0",
- "@node-red/util": "3.1.0-beta.0",
- "async-mutex": "0.3.2",
+ "@node-red/registry": "3.1.0-beta.1",
+ "@node-red/util": "3.1.0-beta.1",
+ "async-mutex": "0.4.0",
"clone": "2.1.2",
- "express": "4.18.1",
+ "express": "4.18.2",
"fs-extra": "10.1.0",
"json-stringify-safe": "5.0.1"
}
diff --git a/packages/node_modules/@node-red/util/package.json b/packages/node_modules/@node-red/util/package.json
index aa82273f6..6833cabf4 100644
--- a/packages/node_modules/@node-red/util/package.json
+++ b/packages/node_modules/@node-red/util/package.json
@@ -1,6 +1,6 @@
{
"name": "@node-red/util",
- "version": "3.1.0-beta.0",
+ "version": "3.1.0-beta.1",
"license": "Apache-2.0",
"repository": {
"type": "git",
@@ -16,11 +16,11 @@
],
"dependencies": {
"fs-extra": "10.1.0",
- "i18next": "21.8.14",
+ "i18next": "21.10.0",
"json-stringify-safe": "5.0.1",
"jsonata": "1.8.6",
"lodash.clonedeep": "^4.5.0",
"moment": "2.29.4",
- "moment-timezone": "0.5.34"
+ "moment-timezone": "0.5.39"
}
}
diff --git a/packages/node_modules/node-red/lib/red.js b/packages/node_modules/node-red/lib/red.js
index ddc140cc4..8ba46a2d3 100644
--- a/packages/node_modules/node-red/lib/red.js
+++ b/packages/node_modules/node-red/lib/red.js
@@ -25,9 +25,15 @@ var api = require("@node-red/editor-api");
var server = null;
var apiEnabled = false;
+const NODE_MAJOR_VERSION = process.versions.node.split('.')[0];
+if (NODE_MAJOR_VERSION > 14) {
+ const dns = require('node:dns');
+ dns.setDefaultResultOrder('ipv4first');
+}
+
function checkVersion(userSettings) {
var semver = require('semver');
- if (!semver.satisfies(process.version,">=8.9.0")) {
+ if (!semver.satisfies(process.version,">=12.0.0")) {
// TODO: in the future, make this a hard error.
// var e = new Error("Unsupported version of Node.js");
// e.code = "unsupported_version";
diff --git a/packages/node_modules/node-red/package.json b/packages/node_modules/node-red/package.json
index b4c4e53f0..ae8aa69bd 100644
--- a/packages/node_modules/node-red/package.json
+++ b/packages/node_modules/node-red/package.json
@@ -1,6 +1,6 @@
{
"name": "node-red",
- "version": "3.1.0-beta.0",
+ "version": "3.1.0-beta.1",
"description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org",
"license": "Apache-2.0",
@@ -31,20 +31,20 @@
"flow"
],
"dependencies": {
- "@node-red/editor-api": "3.1.0-beta.0",
- "@node-red/runtime": "3.1.0-beta.0",
- "@node-red/util": "3.1.0-beta.0",
- "@node-red/nodes": "3.1.0-beta.0",
+ "@node-red/editor-api": "3.1.0-beta.1",
+ "@node-red/runtime": "3.1.0-beta.1",
+ "@node-red/util": "3.1.0-beta.1",
+ "@node-red/nodes": "3.1.0-beta.1",
"basic-auth": "2.0.1",
"bcryptjs": "2.4.3",
- "express": "4.18.1",
+ "express": "4.18.2",
"fs-extra": "10.1.0",
"node-red-admin": "^3.0.0",
"nopt": "5.0.0",
- "semver": "7.3.7"
+ "semver": "7.3.8"
},
"optionalDependencies": {
- "bcrypt": "5.0.1"
+ "bcrypt": "5.1.0"
},
"engines": {
"node": ">=14"
diff --git a/packages/node_modules/node-red/settings.js b/packages/node_modules/node-red/settings.js
index ec247a672..d6e14f21b 100644
--- a/packages/node_modules/node-red/settings.js
+++ b/packages/node_modules/node-red/settings.js
@@ -181,7 +181,7 @@ module.exports = {
/** Some nodes, such as HTTP In, can be used to listen for incoming http requests.
* By default, these are served relative to '/'. The following property
- * can be used to specifiy a different root path. If set to false, this is
+ * can be used to specify a different root path. If set to false, this is
* disabled.
*/
//httpNodeRoot: '/red-nodes',
@@ -219,17 +219,17 @@ module.exports = {
/** When httpAdminRoot is used to move the UI to a different root path, the
* following property can be used to identify a directory of static content
* that should be served at http://localhost:1880/.
- * When httpStaticRoot is set differently to httpAdminRoot, there is no need
+ * When httpStaticRoot is set differently to httpAdminRoot, there is no need
* to move httpAdminRoot
*/
//httpStatic: '/home/nol/node-red-static/', //single static source
/* OR multiple static sources can be created using an array of objects... */
//httpStatic: [
- // {path: '/home/nol/pics/', root: "/img/"},
- // {path: '/home/nol/reports/', root: "/doc/"},
+ // {path: '/home/nol/pics/', root: "/img/"},
+ // {path: '/home/nol/reports/', root: "/doc/"},
//],
- /**
+ /**
* All static routes will be appended to httpStaticRoot
* e.g. if httpStatic = "/home/nol/docs" and httpStaticRoot = "/static/"
* then "/home/nol/docs" will be served at "/static/"
@@ -256,11 +256,11 @@ module.exports = {
*/
// lang: "de",
- /** Configure diagnostics options
+ /** Configure diagnostics options
* - enabled: When `enabled` is `true` (or unset), diagnostics data will
- * be available at http://localhost:1880/diagnostics
- * - ui: When `ui` is `true` (or unset), the action `show-system-info` will
- * be available to logged in users of node-red editor
+ * be available at http://localhost:1880/diagnostics
+ * - ui: When `ui` is `true` (or unset), the action `show-system-info` will
+ * be available to logged in users of node-red editor
*/
diagnostics: {
/** enable or disable diagnostics endpoint. Must be set to `false` to disable */
@@ -268,10 +268,10 @@ module.exports = {
/** enable or disable diagnostics display in the node-red editor. Must be set to `false` to disable */
ui: true,
},
- /** Configure runtimeState options
- * - enabled: When `enabled` is `true` flows runtime can be Started/Stoped
- * by POSTing to available at http://localhost:1880/flows/state
- * - ui: When `ui` is `true`, the action `core:start-flows` and
+ /** Configure runtimeState options
+ * - enabled: When `enabled` is `true` flows runtime can be Started/Stopped
+ * by POSTing to available at http://localhost:1880/flows/state
+ * - ui: When `ui` is `true`, the action `core:start-flows` and
* `core:stop-flows` will be available to logged in users of node-red editor
* Also, the deploy menu (when set to default) will show a stop or start button
*/
@@ -422,7 +422,16 @@ module.exports = {
//fontFamily: "Cascadia Code, Fira Code, Consolas, 'Courier New', monospace",
//fontLigatures: true,
}
- }
+ },
+
+ markdownEditor: {
+ mermaid: {
+ /** enable or disable mermaid diagram in markdown document
+ */
+ enabled: true
+ }
+ },
+
},
/*******************************************************************************
@@ -519,7 +528,7 @@ module.exports = {
*/
//tlsConfigDisableLocalFiles: true,
- /** The following property can be used to verify websocket connection attempts.
+ /** The following property can be used to verify WebSocket connection attempts.
* This allows, for example, the HTTP request headers to be checked to ensure
* they include valid authentication information.
*/
diff --git a/test/nodes/core/common/91-global-config_spec.js b/test/nodes/core/common/91-global-config_spec.js
new file mode 100644
index 000000000..8cc5658cf
--- /dev/null
+++ b/test/nodes/core/common/91-global-config_spec.js
@@ -0,0 +1,47 @@
+var should = require("should");
+var config = require("nr-test-utils").require("@node-red/nodes/core/common/91-global-config.js");
+var inject = require("nr-test-utils").require("@node-red/nodes/core/common/20-inject.js");
+var helper = require("node-red-node-test-helper");
+
+describe('unknown Node', function() {
+
+ afterEach(function() {
+ helper.unload();
+ });
+
+ it('should be loaded', function(done) {
+ var flow = [{id:"n1", type:"global-config", name: "XYZ" }];
+ helper.load(config, flow, function() {
+ var n1 = helper.getNode("n1");
+ n1.should.have.property("name", "XYZ");
+ done();
+ });
+ });
+
+ it('should access global environment variable', function(done) {
+ var flow = [{id:"n1", type:"global-config", name: "XYZ",
+ env: [ {
+ name: "X",
+ type: "string",
+ value: "foo"
+ }]
+ },
+ {id: "n2", type: "inject", topic: "t1", payload: "X", payloadType: "env", wires: [["n3"]], z: "flow"},
+ {id: "n3", type: "helper"}
+ ];
+ helper.load([config, inject], flow, function() {
+ var n2 = helper.getNode("n2");
+ var n3 = helper.getNode("n3");
+ n3.on("input", (msg) => {
+ try {
+ msg.should.have.property("payload", "foo");
+ done();
+ } catch (err) {
+ done(err);
+ }
+ });
+ n2.receive({});
+ });
+ });
+
+});
diff --git a/test/nodes/core/function/15-change_spec.js b/test/nodes/core/function/15-change_spec.js
index b8d7be03b..66f02d6d1 100644
--- a/test/nodes/core/function/15-change_spec.js
+++ b/test/nodes/core/function/15-change_spec.js
@@ -1717,6 +1717,24 @@ describe('change Node', function() {
changeNode1.receive({topic:{foo:{bar:1}}, payload:"String"});
});
});
+ it('moves the value of a message property object to itself', function(done) {
+ var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"move","p":"payload","pt":"msg","to":"payload","tot":"msg"}],"name":"changeNode","wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(changeNode, flow, function() {
+ var changeNode1 = helper.getNode("changeNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ helperNode1.on("input", function(msg) {
+ try {
+ msg.should.have.property('payload');
+ msg.payload.should.equal("bar");
+ done();
+ } catch(err) {
+ done(err);
+ }
+ });
+ changeNode1.receive({payload:"bar"});
+ });
+ });
it('moves the value of a message property object to a sub-property', function(done) {
var flow = [{"id":"changeNode1","type":"change","rules":[{"t":"move","p":"payload","pt":"msg","to":"payload.foo","tot":"msg"}],"name":"changeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
diff --git a/test/nodes/core/function/16-range_spec.js b/test/nodes/core/function/16-range_spec.js
index a0dcd0078..620d21b12 100644
--- a/test/nodes/core/function/16-range_spec.js
+++ b/test/nodes/core/function/16-range_spec.js
@@ -106,6 +106,27 @@ describe('range Node', function() {
genericRangeTest("clamp", 0, 10, 0, 1000, false, -1, 0, done);
});
+ it('drops msg if in drop mode and input outside range', function(done) {
+ var flow = [{"id":"rangeNode1","type":"range","minin":2,"maxin":8,"minout":20,"maxout":80,"action":"drop","round":true,"name":"rangeNode","wires":[["helperNode1"]]},
+ {id:"helperNode1", type:"helper", wires:[]}];
+ helper.load(rangeNode, flow, function() {
+ var rangeNode1 = helper.getNode("rangeNode1");
+ var helperNode1 = helper.getNode("helperNode1");
+ helperNode1.on("input", function(msg) {
+ try {
+ msg.should.have.property('payload');
+ msg.payload.should.equal(50);
+ done();
+ } catch(err) {
+ done(err);
+ }
+ });
+ rangeNode1.receive({payload:1});
+ rangeNode1.receive({payload:9});
+ rangeNode1.receive({payload:5});
+ });
+ });
+
it('just passes on msg if payload not present', function(done) {
var flow = [{"id":"rangeNode1","type":"range","minin":0,"maxin":100,"minout":0,"maxout":100,"action":"scale","round":true,"name":"rangeNode","wires":[["helperNode1"]]},
{id:"helperNode1", type:"helper", wires:[]}];
diff --git a/test/nodes/core/network/21-httprequest_spec.js b/test/nodes/core/network/21-httprequest_spec.js
index 688776289..14f3fd628 100644
--- a/test/nodes/core/network/21-httprequest_spec.js
+++ b/test/nodes/core/network/21-httprequest_spec.js
@@ -31,6 +31,8 @@ var multer = require("multer");
var RED = require("nr-test-utils").require("node-red/lib/red");
var fs = require('fs-extra');
var auth = require('basic-auth');
+const { version } = require("os");
+const net = require('net')
describe('HTTP Request Node', function() {
var testApp;
@@ -1527,7 +1529,7 @@ describe('HTTP Request Node', function() {
msg.payload.headers.should.have.property('Content-Type').which.startWith('application/json');
//msg.dynamicHeaderName should be present in headers with the value of msg.dynamicHeaderValue
msg.payload.headers.should.have.property('dyn-header-name').which.startWith('dyn-header-value');
- //static (custom) header set in Flow UI should be present
+ //static (custom) header set in Flow UI should be present
msg.payload.headers.should.have.property('static-header-name').which.startWith('static-header-value');
//msg.headers['location'] should be deleted because Flow UI "Location" header has a blank value
//ensures headers with matching characters but different case are eliminated
@@ -2265,4 +2267,105 @@ describe('HTTP Request Node', function() {
});
});
});
+
+ describe('should parse broken headers', function() {
+
+ const versions = process.versions.node.split('.')
+
+ if (( versions[0] == 14 && versions[1] >= 20 ) ||
+ ( versions[0] == 16 && versions[1] >= 16 ) ||
+ ( versions[0] == 18 && versions[1] >= 5 ) ||
+ ( versions[0] > 18)) {
+ // only test if on new enough NodeJS version
+
+ let port = testPort++
+
+ let server;
+
+ before(function() {
+ server = net.createServer(function (socket) {
+ socket.write("HTTP/1.0 200\nContent-Type: text/plain\n\nHelloWorld")
+ socket.end()
+ })
+
+ server.listen(port,'127.0.0.1', function(err) {
+ })
+ });
+
+ after(function() {
+ server.close()
+ });
+
+ it('should accept broken headers', function (done) {
+ var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:`http://localhost:${port}/`, insecureHTTPParser: true},
+ {id:"n2", type:"helper"}];
+ helper.load(httpRequestNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n2.on('input', function(msg) {
+ try {
+ msg.payload.should.equal('HelloWorld')
+ done()
+ } catch (err) {
+ done(err)
+ }
+ })
+ n1.receive({payload: 'foo'})
+ });
+ });
+
+ it('should reject broken headers', function (done) {
+ var flow = [{id:'n1',type:'http request',wires:[['n2']],method:'GET',ret:'obj',url:`http://localhost:${port}/`},
+ {id:"n2", type:"helper"}];
+ helper.load(httpRequestNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n2.on('input', function(msg) {
+ try{
+ msg.payload.should.match(/RequestError: Parse Error/)
+ done()
+ } catch (err) {
+ done(err)
+ }
+ })
+ n1.receive({payload: 'foo'})
+
+ });
+ });
+ }
+ });
+
+ describe('multipart form posts', function() {
+ it('should send arrays as multiple entries', function (done) {
+ const flow = [
+ {
+ id: 'n1', type: 'http request', wires: [['n2']], method: 'POST', ret: 'obj', url: getTestURL('/file-upload'), headers: [
+ ]
+ },
+ { id: "n2", type: "helper" }
+ ];
+ helper.load(httpRequestNode, flow, function() {
+ var n1 = helper.getNode("n1");
+ var n2 = helper.getNode("n2");
+ n2.on('input', function(msg){
+ try {
+ msg.payload.body.should.have.property('foo')
+ msg.payload.body.list.should.deepEqual(['a','b','c'])
+ done()
+ } catch (e) {
+ done(e)
+ }
+ });
+ n1.receive({
+ headers: {
+ 'content-type': 'multipart/form-data'
+ },
+ payload: {
+ foo: 'bar',
+ list: [ 'a', 'b', 'c' ]
+ }
+ });
+ })
+ });
+ })
});
diff --git a/test/nodes/core/network/21-mqtt_spec.js b/test/nodes/core/network/21-mqtt_spec.js
index 655cc9c73..f97442b50 100644
--- a/test/nodes/core/network/21-mqtt_spec.js
+++ b/test/nodes/core/network/21-mqtt_spec.js
@@ -27,7 +27,7 @@ describe('MQTT Nodes', function () {
} catch (error) { }
});
- it('should be loaded and have default values', function (done) {
+ it('should be loaded and have default values (MQTT V4)', function (done) {
this.timeout = 2000;
const { flow, nodes } = buildBasicMQTTSendRecvFlow({ id: "mqtt.broker", name: "mqtt_broker", autoConnect: false }, { id: "mqtt.in", topic: "in_topic" }, { id: "mqtt.out", topic: "out_topic" });
helper.load(mqttNodes, flow, function () {
@@ -61,6 +61,52 @@ describe('MQTT Nodes', function () {
mqttBroker.options.clientId.should.containEql('nodered_');
mqttBroker.options.should.have.property('keepalive').type("number");
mqttBroker.options.should.have.property('reconnectPeriod').type("number");
+ //as this is not a v5 connection, ensure v5 properties are not present
+ mqttBroker.options.should.not.have.property('protocolVersion', 5);
+ mqttBroker.options.should.not.have.property('properties');
+ done();
+ } catch (error) {
+ done(error)
+ }
+ });
+ });
+ it('should be loaded and have default values (MQTT V5)', function (done) {
+ this.timeout = 2000;
+ const { flow, nodes } = buildBasicMQTTSendRecvFlow({ id: "mqtt.broker", name: "mqtt_broker", autoConnect: false, cleansession: false, clientid: 'clientid', keepalive: 35, sessionExpiry: '6000', protocolVersion: '5', userProps: {"prop": "val"}}, { id: "mqtt.in", topic: "in_topic" }, { id: "mqtt.out", topic: "out_topic" });
+ helper.load(mqttNodes, flow, function () {
+ try {
+ const mqttIn = helper.getNode("mqtt.in");
+ const mqttOut = helper.getNode("mqtt.out");
+ const mqttBroker = helper.getNode("mqtt.broker");
+
+ should(mqttIn).be.type("object", "mqtt in node should be an object")
+ mqttIn.should.have.property('broker', nodes.mqtt_broker.id); //should be the id of the broker node
+ mqttIn.should.have.property('datatype', 'utf8'); //default: 'utf8'
+ mqttIn.should.have.property('isDynamic', false); //default: false
+ mqttIn.should.have.property('inputs', 0); //default: 0
+ mqttIn.should.have.property('qos', 2); //default: 2
+ mqttIn.should.have.property('topic', "in_topic");
+ mqttIn.should.have.property('wires', [["helper.node"]]);
+
+ should(mqttOut).be.type("object", "mqtt out node should be an object")
+ mqttOut.should.have.property('broker', nodes.mqtt_broker.id); //should be the id of the broker node
+ mqttOut.should.have.property('topic', "out_topic");
+
+ should(mqttBroker).be.type("object", "mqtt broker node should be an object")
+ mqttBroker.should.have.property('broker', BROKER_HOST);
+ mqttBroker.should.have.property('port', BROKER_PORT);
+ mqttBroker.should.have.property('brokerurl');
+ // mqttBroker.should.have.property('autoUnsubscribe', true);//default: true
+ mqttBroker.should.have.property('autoConnect', false);//Set "autoConnect:false" in brokerOptions
+ mqttBroker.should.have.property('options');
+ mqttBroker.options.should.have.property('clean', false);
+ mqttBroker.options.should.have.property('clientId', 'clientid');
+ mqttBroker.options.should.have.property('keepalive').type("number", 35);
+ mqttBroker.options.should.have.property('reconnectPeriod').type("number");
+ //as this IS a v5 connection, ensure v5 properties are not present
+ mqttBroker.options.should.have.property('protocolVersion', 5);
+ mqttBroker.options.should.have.property('properties');
+ mqttBroker.options.properties.should.have.property('sessionExpiryInterval');
done();
} catch (error) {
done(error)
@@ -173,7 +219,7 @@ describe('MQTT Nodes', function () {
topic: nextTopic(),
payload: '{prop:"value3", "num":3}', // send invalid JSON ...
}
- const hooks = { done: done, beforeLoad: null, afterLoad: null, afterConnect: null }
+ const hooks = { done: null, beforeLoad: null, afterLoad: null, afterConnect: null }
hooks.afterLoad = (helperNode, mqttBroker, mqttIn, mqttOut) => {
helperNode.on("input", function (msg) {
try {
@@ -299,7 +345,7 @@ describe('MQTT Nodes', function () {
topic: nextTopic(),
payload: '{prop:"value3", "num":3}', contentType: "application/json", // send invalid JSON ...
}
- const hooks = { done: done, beforeLoad: null, afterLoad: null, afterConnect: null }
+ const hooks = { done: null, beforeLoad: null, afterLoad: null, afterConnect: null }
hooks.afterLoad = (helperNode, mqttBroker, mqttIn, mqttOut) => {
helperNode.on("input", function (msg) {
try {
@@ -385,7 +431,7 @@ describe('MQTT Nodes', function () {
if (skipTests) { return this.skip() }
this.timeout = 2000;
const options = {}
- const hooks = { done: done, beforeLoad: null, afterLoad: null, afterConnect: null }
+ const hooks = { beforeLoad: null, afterLoad: null, afterConnect: null }
hooks.beforeLoad = (flow) => { //add a status node pointed at MQTT Out node (to watch for connection status change)
flow.push({ "id": "status.node", "type": "status", "name": "status_node", "scope": ["mqtt.out"], "wires": [["helper.node"]] });//add status node to watch mqtt_out
}
@@ -416,20 +462,66 @@ describe('MQTT Nodes', function () {
this.timeout = 2000;
const baseTopic = nextTopic();
const brokerOptions = {
+ autoConnect: false,
protocolVersion: 4,
birthTopic: baseTopic + "/birth",
- birthPayload: "broker connected",
+ birthPayload: "broker birth",
birthQos: 2,
}
- const options = {};
- const hooks = { done: done, beforeLoad: null, afterLoad: null, afterConnect: null };
- options.expectMsg = {
+ const expectMsg = {
topic: brokerOptions.birthTopic,
payload: brokerOptions.birthPayload,
qos: brokerOptions.birthQos
};
+ const options = { };
+ const hooks = { };
+ hooks.afterLoad = (helperNode, mqttBroker, mqttIn, mqttOut) => {
+ helperNode.on("input", function (msg) {
+ try {
+ compareMsgToExpected(msg, expectMsg);
+ done();
+ } catch (error) {
+ done(error)
+ }
+ })
+ mqttIn.receive({ "action": "connect" }); //now request connect action
+ return true; //handled
+ }
testSendRecv(brokerOptions, { topic: brokerOptions.birthTopic }, {}, options, hooks);
});
+ itConditional('should safely discard bad birth topic', function (done) {
+ if (skipTests) { return this.skip() }
+ this.timeout = 2000;
+ const baseTopic = nextTopic();
+ const brokerOptions = {
+ protocolVersion: 4,
+ birthTopic: baseTopic + "#", // a publish topic should never have a wildcard
+ birthPayload: "broker connected",
+ birthQos: 2,
+ }
+ const options = {};
+ const hooks = { done: null, beforeLoad: null, afterLoad: null, afterConnect: null };
+ hooks.afterLoad = (helperNode, mqttBroker, mqttIn, mqttOut) => {
+ helperNode.on("input", function (msg) {
+ try {
+ msg.should.have.a.property("error").type("object");
+ msg.error.should.have.a.property("source").type("object");
+ msg.error.source.should.have.a.property("id", mqttIn.id);
+ done();
+ } catch (err) {
+ done(err)
+ }
+ });
+ return true; //handled
+ }
+ options.expectMsg = null;
+ try {
+ testSendRecv(brokerOptions, { topic: brokerOptions.birthTopic }, {}, options, hooks);
+ done()
+ } catch(err) {
+ done(e)
+ }
+ });
itConditional('should publish close message', function (done) {
if (skipTests) { return this.skip() }
this.timeout = 2000;
@@ -587,12 +679,13 @@ function testSendRecv(brokerOptions, inNodeOptions, outNodeOptions, options, hoo
const mqttBroker = helper.getNode(brokerOptions.id);
const mqttIn = helper.getNode(nodes.mqtt_in.id);
const mqttOut = helper.getNode(nodes.mqtt_out.id);
- let afterLoadHandled = false;
+ let afterLoadHandled = false, finished = false;
if (hooks.afterLoad) {
afterLoadHandled = hooks.afterLoad(helperNode, mqttBroker, mqttIn, mqttOut)
}
if (!afterLoadHandled) {
helperNode.on("input", function (msg) {
+ finished = true
try {
compareMsgToExpected(msg, expectMsg);
if (hooks.done) { hooks.done(); }
@@ -617,10 +710,12 @@ function testSendRecv(brokerOptions, inNodeOptions, outNodeOptions, options, hoo
}
})
.catch((e) => {
+ if(finished) { return }
if (hooks.done) { hooks.done(e); }
else { throw e; }
});
} catch (err) {
+ if(finished) { return }
if (hooks.done) { hooks.done(err); }
else { throw err; }
}
@@ -666,6 +761,7 @@ function buildMQTTBrokerNode(id, name, brokerHost, brokerPort, options) {
node.cleansession = String(options.cleansession) == "false" ? false : true;
node.autoUnsubscribe = String(options.autoUnsubscribe) == "false" ? false : true;
node.autoConnect = String(options.autoConnect) == "false" ? false : true;
+ node.sessionExpiry = options.sessionExpiry ? options.sessionExpiry : undefined;
if (options.birthTopic) {
node.birthTopic = options.birthTopic;
@@ -760,8 +856,8 @@ function waitBrokerConnect(broker, timeLimit) {
let waitConnected = (broker, timeLimit) => {
const brokers = Array.isArray(broker) ? broker : [broker];
timeLimit = timeLimit || 1000;
- let timer, resolved = false;
return new Promise( (resolve, reject) => {
+ let timer, resolved = false;
timer = wait();
function wait() {
if (brokers.every(e => e.connected == true)) {
diff --git a/test/nodes/core/parsers/70-CSV_spec.js b/test/nodes/core/parsers/70-CSV_spec.js
index 93d59a171..681711b3b 100644
--- a/test/nodes/core/parsers/70-CSV_spec.js
+++ b/test/nodes/core/parsers/70-CSV_spec.js
@@ -693,19 +693,20 @@ 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,f", wires:[["n2"]] },
+ var flow = [ { id:"n1", type:"csv", temp:"a,b,c,,e,f,g,h,i,j,k", 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) {
+ // console.log("GOT",msg)
try {
- msg.should.have.property('payload', '4,foo,true,,0,"Hello\nWorld"\n');
+ msg.should.have.property('payload', '4,foo,true,,0,"Hello\nWorld",,,undefined,null,null\n');
done();
}
catch(e) { done(e); }
});
- var testJson = { e:0, d:1, b:"foo", c:true, a:4, f:"Hello\nWorld" };
+ var testJson = { e:0, d:1, b:"foo", c:true, a:4, f:"Hello\nWorld", h:undefined, i:"undefined",j:null,k:"null" };
n1.emit("input", {payload:testJson});
});
});
@@ -717,13 +718,14 @@ describe('CSV node', function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
+ // console.log("GOT",msg)
try {
- msg.should.have.property('payload', '1,foo,"ba""r","di,ng"\n');
+ msg.should.have.property('payload', '1,foo,"ba""r","di,ng",,undefined,null\n');
done();
}
catch(e) { done(e); }
});
- var testJson = { d:1, b:"foo", c:"ba\"r", a:"di,ng" };
+ var testJson = { d:1, b:"foo", c:"ba\"r", a:"di,ng", e:undefined, f:"undefined", g:null,h:"null" };
n1.emit("input", {payload:testJson});
});
});
@@ -764,6 +766,33 @@ describe('CSV node', function() {
});
});
+ it('should handle a template with quotes in the property names', function(done) {
+ var flow = [ { id:"n1", type:"csv", temp:"", hdrout:"all", 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', 'a"a,b\'b\nA1,B1\nA2,B2\n');
+ done();
+ }
+ catch(e) { done(e); }
+ });
+ var testJson = [
+ {
+ "a\"a": "A1",
+ "b'b": "B1"
+ },
+ {
+ "a\"a": "A2",
+ "b'b": "B2"
+ }
+ ]
+ n1.emit("input", {payload:testJson});
+ });
+ });
+
it('should convert an array of objects to a multi-line csv', function(done) {
var flow = [ { id:"n1", type:"csv", temp:"a,d,c,b", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
diff --git a/test/nodes/core/storage/23-watch_spec.js b/test/nodes/core/storage/23-watch_spec.js
index a9942d5b1..dd7b94037 100644
--- a/test/nodes/core/storage/23-watch_spec.js
+++ b/test/nodes/core/storage/23-watch_spec.js
@@ -15,11 +15,14 @@
**/
var fs = require("fs-extra");
+var os = require("os");
var path = require("path");
var should = require("should");
var helper = require("node-red-node-test-helper");
var watchNode = require("nr-test-utils").require("@node-red/nodes/core/storage/23-watch.js");
+var arch = os.arch();
+var platform = os.platform();
describe('watch Node', function() {
this.timeout(5000);
@@ -89,7 +92,10 @@ describe('watch Node', function() {
msg.should.have.property('payload', result.payload);
msg.should.have.property('type', result.type);
if('size' in result) {
- msg.should.have.property('size', result.size);
+ if (!((arch === "arm64") && (platform === "darwin"))) {
+ // On OSX/ARM, two change events occur and first event do not reflect file size change. So ignore size field in the case.
+ msg.should.have.property('size', result.size);
+ }
}
count++;
if(count === len) {
diff --git a/test/unit/@node-red/editor-api/lib/index_spec.js b/test/unit/@node-red/editor-api/lib/index_spec.js
index 7308fdc2d..37bb82c13 100644
--- a/test/unit/@node-red/editor-api/lib/index_spec.js
+++ b/test/unit/@node-red/editor-api/lib/index_spec.js
@@ -61,7 +61,7 @@ describe("api/index", function() {
should.not.exist(api.httpAdmin);
done();
});
- describe('initalises admin api without adminAuth', function(done) {
+ describe('initalises admin api without adminAuth', function() {
before(function() {
beforeEach();
api.init({},{},{},{});
@@ -78,7 +78,7 @@ describe("api/index", function() {
})
});
- describe('initalises admin api without editor', function(done) {
+ describe('initalises admin api without editor', function() {
before(function() {
beforeEach();
api.init({ disableEditor: true },{},{},{});
@@ -95,7 +95,7 @@ describe("api/index", function() {
})
});
- describe('initialises api with admin middleware', function(done) {
+ describe('initialises api with admin middleware', function() {
it('ignores non-function values',function(done) {
api.init({ httpAdminRoot: true, httpAdminMiddleware: undefined },{},{},{});
const middlewareFound = api.httpAdmin._router.stack.filter((layer) => layer.name === 'testMiddleware')
@@ -112,7 +112,7 @@ describe("api/index", function() {
});
});
- describe('initialises api with authentication enabled', function(done) {
+ describe('initialises api with authentication enabled', function() {
it('enables an oauth/openID based authentication mechanism',function(done) {
const stub = sinon.stub(apiAuth, 'genericStrategy').callsFake(function(){});
@@ -135,7 +135,7 @@ describe("api/index", function() {
});
- describe('initialises api with custom cors config', function (done) {
+ describe('initialises api with custom cors config', function () {
const httpAdminCors = {
origin: "*",
methods: "GET,PUT,POST,DELETE"
@@ -156,7 +156,7 @@ describe("api/index", function() {
})
});
- describe('editor start', function (done) {
+ describe('editor start', function () {
it('cannot be started when editor is disabled', function (done) {
const stub = sinon.stub(apiEditor, 'start').callsFake(function () {
diff --git a/test/unit/@node-red/registry/lib/localfilesystem_spec.js b/test/unit/@node-red/registry/lib/localfilesystem_spec.js
index 3b1bb63f3..3ce74c158 100644
--- a/test/unit/@node-red/registry/lib/localfilesystem_spec.js
+++ b/test/unit/@node-red/registry/lib/localfilesystem_spec.js
@@ -329,17 +329,36 @@ describe("red/nodes/registry/localfilesystem",function() {
localfilesystem.init({nodesDir:[nodesDir2]});
const nodeModule = localfilesystem.getModuleFiles();
const loaded = Object.keys(nodeModule)
- loaded.should.have.a.property("length", 3)
loaded.indexOf('@test/testnode').should.greaterThan(-1, "Should load @test/testnode")
+ loaded.indexOf('lower-case').should.greaterThan(-1, "Should load lower-case")
+ loaded.indexOf('@lowercase/lower-case2').should.greaterThan(-1, "Should load @lowercase/lower-case2")
loaded.indexOf('testnode2').should.greaterThan(-1, "Should load testnode2")
loaded.indexOf('test-theme2').should.greaterThan(-1, "Should load test-theme2")
+ loaded.should.have.a.property("length", 5)
+ // scoped module with nodes in same dir as package.json
nodeModule['@test/testnode'].should.have.a.property('name','@test/testnode');
nodeModule['@test/testnode'].should.have.a.property('version','1.0.0');
nodeModule['@test/testnode'].should.have.a.property('nodes');
nodeModule['@test/testnode'].should.have.a.property('path');
nodeModule['@test/testnode'].should.have.a.property('user', false);
+ // node-red module with nodes in sub dir
+ nodeModule['@lowercase/lower-case2'].should.have.a.property('name','@lowercase/lower-case2');
+ nodeModule['@lowercase/lower-case2'].should.have.a.property('version','2.0.0');
+ nodeModule['@lowercase/lower-case2'].should.have.a.property('nodes');
+ nodeModule['@lowercase/lower-case2'].nodes.should.have.a.property('lower-case');
+ nodeModule['@lowercase/lower-case2'].should.have.a.property('path');
+ nodeModule['@lowercase/lower-case2'].should.have.a.property('user', false);
+
+ // scoped module with nodes in sub dir
+ nodeModule['lower-case'].should.have.a.property('name', 'lower-case');
+ nodeModule['lower-case'].should.have.a.property('version','1.0.0');
+ nodeModule['lower-case'].should.have.a.property('nodes');
+ nodeModule['lower-case'].nodes.should.have.a.property('lower-case');
+ nodeModule['lower-case'].should.have.a.property('path');
+ nodeModule['lower-case'].should.have.a.property('user', false);
+
nodeModule['testnode2'].should.have.a.property('name','testnode2');
nodeModule['testnode2'].should.have.a.property('version','1.0.0');
nodeModule['testnode2'].should.have.a.property('nodes');
diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir2/@lower-case2/lower-case2/lower-case.html b/test/unit/@node-red/registry/lib/resources/nodesDir2/@lower-case2/lower-case2/lower-case.html
new file mode 100644
index 000000000..617f48491
--- /dev/null
+++ b/test/unit/@node-red/registry/lib/resources/nodesDir2/@lower-case2/lower-case2/lower-case.html
@@ -0,0 +1,26 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir2/@lower-case2/lower-case2/lower-case.js b/test/unit/@node-red/registry/lib/resources/nodesDir2/@lower-case2/lower-case2/lower-case.js
new file mode 100644
index 000000000..73579ba04
--- /dev/null
+++ b/test/unit/@node-red/registry/lib/resources/nodesDir2/@lower-case2/lower-case2/lower-case.js
@@ -0,0 +1,11 @@
+module.exports = function(RED) {
+function LowerCaseNode(config) {
+ RED.nodes.createNode(this,config);
+ var node = this;
+ node.on('input', function(msg) {
+ msg.payload = msg.payload.toLowerCase();
+ node.send(msg);
+ });
+ }
+ RED.nodes.registerType("lower-case2",LowerCaseNode);
+}
\ No newline at end of file
diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir2/@lower-case2/package.json b/test/unit/@node-red/registry/lib/resources/nodesDir2/@lower-case2/package.json
new file mode 100644
index 000000000..6b6ce9aa9
--- /dev/null
+++ b/test/unit/@node-red/registry/lib/resources/nodesDir2/@lower-case2/package.json
@@ -0,0 +1,9 @@
+{
+ "name" : "@lowercase/lower-case2",
+ "node-red" : {
+ "nodes": {
+ "lower-case": "lower-case2/lower-case.js"
+ }
+ },
+ "version": "2.0.0"
+}
\ No newline at end of file
diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir2/lower-case/lower-case/lower-case.html b/test/unit/@node-red/registry/lib/resources/nodesDir2/lower-case/lower-case/lower-case.html
new file mode 100644
index 000000000..e57d51131
--- /dev/null
+++ b/test/unit/@node-red/registry/lib/resources/nodesDir2/lower-case/lower-case/lower-case.html
@@ -0,0 +1,26 @@
+
+
+
+
+
diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir2/lower-case/lower-case/lower-case.js b/test/unit/@node-red/registry/lib/resources/nodesDir2/lower-case/lower-case/lower-case.js
new file mode 100644
index 000000000..006b35eb6
--- /dev/null
+++ b/test/unit/@node-red/registry/lib/resources/nodesDir2/lower-case/lower-case/lower-case.js
@@ -0,0 +1,11 @@
+module.exports = function(RED) {
+ function LowerCaseNode(config) {
+ RED.nodes.createNode(this,config);
+ var node = this;
+ node.on('input', function(msg) {
+ msg.payload = msg.payload.toLowerCase();
+ node.send(msg);
+ });
+ }
+ RED.nodes.registerType("lower-case",LowerCaseNode);
+}
diff --git a/test/unit/@node-red/registry/lib/resources/nodesDir2/lower-case/package.json b/test/unit/@node-red/registry/lib/resources/nodesDir2/lower-case/package.json
new file mode 100644
index 000000000..a632eaddd
--- /dev/null
+++ b/test/unit/@node-red/registry/lib/resources/nodesDir2/lower-case/package.json
@@ -0,0 +1,9 @@
+{
+ "name" : "lower-case",
+ "node-red" : {
+ "nodes": {
+ "lower-case": "lower-case/lower-case.js"
+ }
+ },
+ "version": "1.0.0"
+}
diff --git a/test/unit/@node-red/runtime/lib/api/diagnostics_spec.js b/test/unit/@node-red/runtime/lib/api/diagnostics_spec.js
index 07e499344..136e4826c 100644
--- a/test/unit/@node-red/runtime/lib/api/diagnostics_spec.js
+++ b/test/unit/@node-red/runtime/lib/api/diagnostics_spec.js
@@ -33,6 +33,11 @@ describe("runtime-api/diagnostics", function() {
flowFile: "flows.json",
mqttReconnectTime: 321,
serialReconnectTime: 432,
+ socketReconnectTime: 2222,
+ socketTimeout: 3333,
+ tcpMsgQueueSize: 4444,
+ inboundWebSocketTimeout: 5555,
+ runtimeState: {enabled: true, ui: false},
adminAuth: {},//should be sanitised to "SET"
httpAdminRoot: "/admin/root/",
httpAdminCors: {},//should be sanitised to "SET"
@@ -45,6 +50,7 @@ describe("runtime-api/diagnostics", function() {
uiHost: "something.secret.com",//should be sanitised to "SET"
uiPort: 1337,//should be sanitised to "SET"
userDir: "/var/super/secret/",//should be sanitised to "SET",
+ nodesDir: "/var/super/secret/",//should be sanitised to "SET",
contextStorage: {
default : { module: "memory" },
file: { module: "localfilesystem" },
@@ -73,8 +79,9 @@ describe("runtime-api/diagnostics", function() {
//result.runtime.xxxxx
const runtimeCount = Object.keys(result.runtime).length;
- runtimeCount.should.eql(4);//ensure no more than 4 keys are present in runtime
+ runtimeCount.should.eql(5);//ensure 5 keys are present in runtime
result.runtime.should.have.property('isStarted',true)
+ result.runtime.should.have.property('flows')
result.runtime.should.have.property('modules').type("object");
result.runtime.should.have.property('settings').type("object");
result.runtime.should.have.property('version','7.7.7');
@@ -87,7 +94,7 @@ describe("runtime-api/diagnostics", function() {
//result.runtime.settings.xxxxx
const settingsCount = Object.keys(result.runtime.settings).length;
- settingsCount.should.eql(21);//ensure no more than the 21 settings listed below are present in the settings object
+ settingsCount.should.eql(27);//ensure no more than the 21 settings listed below are present in the settings object
result.runtime.settings.should.have.property('available',true);
result.runtime.settings.should.have.property('apiMaxLength', "UNSET");//deliberately disabled to ensure UNSET is returned
result.runtime.settings.should.have.property('debugMaxLength', 1111);
@@ -96,6 +103,11 @@ describe("runtime-api/diagnostics", function() {
result.runtime.settings.should.have.property('flowFile', "flows.json");
result.runtime.settings.should.have.property('mqttReconnectTime', 321);
result.runtime.settings.should.have.property('serialReconnectTime', 432);
+ result.runtime.settings.should.have.property('socketReconnectTime', 2222);
+ result.runtime.settings.should.have.property('socketTimeout', 3333);
+ result.runtime.settings.should.have.property('tcpMsgQueueSize', 4444);
+ result.runtime.settings.should.have.property('inboundWebSocketTimeout', 5555);
+ result.runtime.settings.should.have.property('runtimeState', {enabled: true, ui: false});
result.runtime.settings.should.have.property("adminAuth", "SET"); //should be sanitised to "SET"
result.runtime.settings.should.have.property("httpAdminCors", "SET"); //should be sanitised to "SET"
result.runtime.settings.should.have.property('httpAdminRoot', "/admin/root/");
@@ -109,6 +121,7 @@ describe("runtime-api/diagnostics", function() {
result.runtime.settings.should.have.property("uiPort", "SET"); //should be sanitised to "SET"
result.runtime.settings.should.have.property("userDir", "SET"); //should be sanitised to "SET"
result.runtime.settings.should.have.property('contextStorage').type("object");
+ result.runtime.settings.should.have.property('nodesDir', "SET")
//result.runtime.settings.contextStorage.xxxxx
const contextCount = Object.keys(result.runtime.settings.contextStorage).length;