mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Merge branch 'dev' into fix_html_tags
This commit is contained in:
		
							
								
								
									
										2
									
								
								.github/workflows/tests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/tests.yml
									
									
									
									
										vendored
									
									
								
							| @@ -16,7 +16,7 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     strategy: | ||||
|       matrix: | ||||
|         node-version: [18, 20, 22] | ||||
|         node-version: [18, 20, 22.4.x] | ||||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
|     - name: Use Node.js ${{ matrix.node-version }} | ||||
|   | ||||
							
								
								
									
										35
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,3 +1,38 @@ | ||||
| #### 4.0.2: Maintenance Release | ||||
|  | ||||
| Editor | ||||
|  | ||||
|  - Use a more subtle border on the header (#4818) @bonanitech | ||||
|  - Improve the editor's French translations (#4824) @GogoVega | ||||
|  - Clean up orphaned editors (#4821) @Steve-Mcl | ||||
|  - Fix node validation if the property is not required (#4812) @GogoVega | ||||
|  - Ensure mermaid.min.js is cached properly between loads of the editor (#4817) @knolleary | ||||
|  | ||||
| Runtime | ||||
|  | ||||
|  - Allow auth cookie name to be customised (#4815) @knolleary | ||||
|  - Guard against undefined sessions in multiplayer (#4816) @knolleary | ||||
|  | ||||
| #### 4.0.1: Maintenance Release | ||||
|  | ||||
| Editor | ||||
|  | ||||
|  - Ensure subflow instance credential property values are extracted (#4802) @knolleary | ||||
|  - Use `_ADD_` value for both `add new...` and `none` options (#4800) @GogoVega | ||||
|  - Fix the config node select value assignment (#4788) @GogoVega | ||||
|  - Add tooltip for number of subflow instance on info tab (#4786) @kazuhitoyokoi | ||||
|  - Add Japanese translations for v4.0.0 (#4785) @kazuhitoyokoi | ||||
|  | ||||
| Runtime | ||||
|  | ||||
|  - Ensure group nodes are properly exported in /flow api (#4803) @knolleary | ||||
|  | ||||
|  Nodes | ||||
|  | ||||
|  - Joins: make using msg.parts optional in join node (#4796) @dceejay | ||||
|  - HTTP Request: UI proxy should setup agents for both http_proxy and https_proxy (#4794) @Steve-Mcl | ||||
|  - HTTP Request: Remove default user agent (#4791) @Steve-Mcl | ||||
|  | ||||
| #### 4.0.0: Milestone Release | ||||
|  | ||||
| This marks the next major release of Node-RED. The following changes represent | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "node-red", | ||||
|     "version": "4.0.0", | ||||
|     "version": "4.1.0-beta.0", | ||||
|     "description": "Low-code programming for event-driven applications", | ||||
|     "homepage": "https://nodered.org", | ||||
|     "license": "Apache-2.0", | ||||
|   | ||||
| @@ -182,6 +182,10 @@ function genericStrategy(adminApp,strategy) { | ||||
|             maxAge: null, | ||||
|             ...settings.httpAdminCookieOptions | ||||
|         } | ||||
|         if (sessionOptions.cookie.name){ | ||||
|             sessionOptions.name = sessionOptions.cookie.name | ||||
|             delete sessionOptions.cookie.name | ||||
|         } | ||||
|     } | ||||
|     adminApp.use(session(sessionOptions)); | ||||
|     //TODO: all passport references ought to be in ./auth | ||||
| @@ -217,10 +221,10 @@ function genericStrategy(adminApp,strategy) { | ||||
|     adminApp.get('/auth/strategy', | ||||
|         passport.authenticate(strategy.name, { | ||||
|             session:false, | ||||
|             failureMessage: true, | ||||
|             failureRedirect: settings.httpAdminRoot + '?session_message=Login Failed' | ||||
|             failWithError: true, | ||||
|             failureMessage: true | ||||
|         }), | ||||
|         completeGenerateStrategyAuth, | ||||
|         completeGenericStrategyAuth, | ||||
|         handleStrategyError | ||||
|     ); | ||||
|  | ||||
| @@ -232,14 +236,14 @@ function genericStrategy(adminApp,strategy) { | ||||
|         passport.authenticate(strategy.name, { | ||||
|             session:false, | ||||
|             failureMessage: true, | ||||
|             failureRedirect: settings.httpAdminRoot + '?session_message=Login Failed' | ||||
|             failWithError: true | ||||
|         }), | ||||
|         completeGenerateStrategyAuth, | ||||
|         completeGenericStrategyAuth, | ||||
|         handleStrategyError | ||||
|     ); | ||||
|  | ||||
| } | ||||
| function completeGenerateStrategyAuth(req,res) { | ||||
| function completeGenericStrategyAuth(req,res) { | ||||
|     var tokens = req.user.tokens; | ||||
|     delete req.user.tokens; | ||||
|     // Successful authentication, redirect home. | ||||
| @@ -249,6 +253,8 @@ function handleStrategyError(err, req, res, next) { | ||||
|     if (res.headersSent) { | ||||
|         return next(err) | ||||
|     } | ||||
|     // Remove the header that passport auto-adds as we don't need it | ||||
|     res.removeHeader('WWW-Authenticate') | ||||
|     log.audit({event: "auth.login.fail.oauth",error:err.toString()}); | ||||
|     res.redirect(settings.httpAdminRoot + '?session_message='+err.toString()); | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@node-red/editor-api", | ||||
|     "version": "4.0.0", | ||||
|     "version": "4.1.0-beta.0", | ||||
|     "license": "Apache-2.0", | ||||
|     "main": "./lib/index.js", | ||||
|     "repository": { | ||||
| @@ -16,8 +16,8 @@ | ||||
|         } | ||||
|     ], | ||||
|     "dependencies": { | ||||
|         "@node-red/util": "4.0.0", | ||||
|         "@node-red/editor-client": "4.0.0", | ||||
|         "@node-red/util": "4.1.0-beta.0", | ||||
|         "@node-red/editor-client": "4.1.0-beta.0", | ||||
|         "bcryptjs": "2.4.3", | ||||
|         "body-parser": "1.20.2", | ||||
|         "clone": "2.1.2", | ||||
|   | ||||
| @@ -27,7 +27,8 @@ | ||||
|       "lock": "Verrouiller", | ||||
|       "unlock": "Déverrouiller", | ||||
|       "locked": "Verrouillé", | ||||
|       "unlocked": "Déverrouillé" | ||||
|       "unlocked": "Déverrouillé", | ||||
|       "format": "Format" | ||||
|     }, | ||||
|     "type": { | ||||
|       "string": "chaîne de caractères", | ||||
| @@ -54,10 +55,10 @@ | ||||
|   "workspace": { | ||||
|     "defaultName": "Flux __number__", | ||||
|     "editFlow": "Modifier le flux : __name__", | ||||
|     "confirmDelete": "Confirmation de la suppression", | ||||
|     "delete": "Etes-vous sûr de vouloir supprimer '__label__'?", | ||||
|     "dropFlowHere": "Déposer le flux ici", | ||||
|     "dropImageHere": "Déposer l'image ici", | ||||
|     "confirmDelete": "Confirmer la suppression", | ||||
|     "delete": "Êtes-vous sûr de vouloir supprimer '__label__' ?", | ||||
|     "dropFlowHere": "Lâchez le flux ici", | ||||
|     "dropImageHere": "Lâchez l'image ici", | ||||
|     "addFlow": "Ajouter un flux", | ||||
|     "addFlowToRight": "Ajouter un flux à droite", | ||||
|     "closeFlow": "Fermer le flux", | ||||
| @@ -74,7 +75,7 @@ | ||||
|     "enabled": "Activé", | ||||
|     "disabled": "Désactivé", | ||||
|     "info": "Description", | ||||
|     "selectNodes": "Cliquer sur les noeuds pour sélectionner", | ||||
|     "selectNodes": "Cliquer pour sélectionner", | ||||
|     "enableFlow": "Activer le flux", | ||||
|     "disableFlow": "Désactiver le flux", | ||||
|     "lockFlow": "Verrouiller le flux", | ||||
| @@ -98,7 +99,7 @@ | ||||
|         "rtl": "De droite à gauche", | ||||
|         "auto": "Contextuel", | ||||
|         "language": "Langue", | ||||
|         "browserDefault": "Navigateur par défaut" | ||||
|         "browserDefault": "Par défaut du Navigateur" | ||||
|       }, | ||||
|       "sidebar": { | ||||
|         "show": "Afficher la barre latérale" | ||||
| @@ -134,7 +135,7 @@ | ||||
|       "disableSelectedNodes": "Désactiver les noeuds sélectionnés", | ||||
|       "showSelectedNodeLabels": "Afficher les étiquettes des noeuds sélectionnés", | ||||
|       "hideSelectedNodeLabels": "Masquer les étiquettes des noeuds sélectionnés", | ||||
|       "showWelcomeTours": "Afficher les visites guidées pour les nouvelles versions", | ||||
|       "showWelcomeTours": "Afficher les visites guidées des nouvelles versions", | ||||
|       "help": "Site web de Node-RED", | ||||
|       "projects": "Projets", | ||||
|       "projects-new": "Nouveau projet", | ||||
| @@ -143,7 +144,7 @@ | ||||
|       "showNodeLabelDefault": "Afficher l'étiquette des noeuds nouvellement ajoutés", | ||||
|       "codeEditor": "Éditeur de code", | ||||
|       "groups": "Groupes", | ||||
|       "groupSelection": "Grouper cette sélection", | ||||
|       "groupSelection": "Grouper la sélection", | ||||
|       "ungroupSelection": "Dégrouper la sélection", | ||||
|       "groupMergeSelection": "Fusionner la sélection", | ||||
|       "groupRemoveSelection": "Supprimer du groupe", | ||||
| @@ -155,7 +156,7 @@ | ||||
|       "alignMiddle": "Aligner au milieu", | ||||
|       "alignBottom": "Aligner en bas", | ||||
|       "distributeHorizontally": "Répartir horizontalement", | ||||
|       "distributeVertically": "Distribuer verticalement", | ||||
|       "distributeVertically": "Répartir verticalement", | ||||
|       "moveToBack": "Déplacer vers l'arrière", | ||||
|       "moveToFront": "Déplacer vers l'avant", | ||||
|       "moveBackwards": "Reculer", | ||||
| @@ -163,21 +164,21 @@ | ||||
|     } | ||||
|   }, | ||||
|   "actions": { | ||||
|     "toggle-navigator": "Basculer de navigateur", | ||||
|     "zoom-out": "Dézoomer", | ||||
|     "zoom-reset": "Réinitialiser le zoom", | ||||
|     "toggle-navigator": "Basculer l'affichage du navigateur", | ||||
|     "zoom-out": "Réduire", | ||||
|     "zoom-reset": "Réinitialiser", | ||||
|     "zoom-in": "Agrandir", | ||||
|     "search-flows": "Rechercher le flux", | ||||
|     "search-prev": "Précédent", | ||||
|     "search-next": "Suivant", | ||||
|     "search-counter": "\"__term__\" __result__ de __count__" | ||||
|     "search-counter": "\"__term__\" __result__ sur __count__" | ||||
|   }, | ||||
|   "user": { | ||||
|     "loggedInAs": "Connecté en tant que __name__", | ||||
|     "username": "Nom d'utilisateur", | ||||
|     "password": "Mot de passe", | ||||
|     "login": "Connexion", | ||||
|     "loginFailed": "Échec de la connexion", | ||||
|     "login": "Se connecter", | ||||
|     "loginFailed": "Échec de connexion", | ||||
|     "notAuthorized": "Pas autorisé", | ||||
|     "errors": { | ||||
|       "settings": "Vous devez être connecté pour accéder aux paramètres", | ||||
| @@ -193,16 +194,16 @@ | ||||
|     "warning": "<strong>Attention</strong> : __message__", | ||||
|     "warnings": { | ||||
|       "undeployedChanges": "Le noeud a des modifications non déployées", | ||||
|       "nodeActionDisabled": "Actions de noeud désactivées", | ||||
|       "nodeActionDisabledSubflow": "Actions de noeud désactivées dans le sous-flux", | ||||
|       "nodeActionDisabled": "Les actions du noeud sont désactivées", | ||||
|       "nodeActionDisabledSubflow": "Les actions de noeud sont désactivées à l'intérieur du sous-flux", | ||||
|       "missing-types": "<p>Flux arrêtés en raison de types de noeuds manquants.</p>", | ||||
|       "missing-modules": "<p>Flux arrêtés en raison de modules manquants.</p>", | ||||
|       "safe-mode": "<p>Flux arrêtés en mode sans échec.</p><p>Vous pouvez modifier vos flux et déployer les changements pour redémarrer.</p>", | ||||
|       "safe-mode": "<p>Flux arrêtés en mode sans échec.</p><p>Vous pouvez modifier vos flux et déployer ensuite les changements afin de démarrer vos flux.</p>", | ||||
|       "restartRequired": "Node-RED doit être redémarré pour mettre à jour les modules", | ||||
|       "credentials_load_failed": "<p>Les flux se sont arrêtés car les informations d'identification n'ont pas pu être déchiffrées.</p><p>Le fichier d'informations d'identification du flux est chiffré, mais la clé de chiffrement du projet est manquante ou invalide.</p>", | ||||
|       "credentials_load_failed_reset": "<p>Les informations d'identification n'ont pas pu être déchiffrées</p><p>Le fichier d'informations d'identification du flux est chiffré, mais la clé de chiffrement du projet est manquante ou invalide.</p><p>Le fichier d'informations d'identification du flux sera réinitialisé lors du prochain déploiement. Toutes les informations d'identification de flux existantes seront perdues.</p>", | ||||
|       "credentials_load_failed": "<p>Les flux se sont arrêtés car les informations d'identification n'ont pas pu être déchiffrées.</p><p>Le fichier d'informations d'identification du flux est chiffré mais la clé de chiffrement du projet est manquante ou invalide.</p>", | ||||
|       "credentials_load_failed_reset": "<p>Les informations d'identification n'ont pas pu être déchiffrées</p><p>Le fichier d'informations d'identification du flux est chiffré mais la clé de chiffrement du projet est manquante ou invalide.</p><p>Le fichier d'informations d'identification du flux sera réinitialisé lors du prochain déploiement. Toutes les informations d'identification des flux existants seront perdues.</p>", | ||||
|       "missing_flow_file": "<p>Fichier contenant les flux introuvable.</p><p>Le projet n'est pas configuré avec un fichier de flux.</p>", | ||||
|       "missing_package_file": "<p>Fichier de paquetage du projet introuvable.</p><p>Il manque au projet un fichier package.json.</p>", | ||||
|       "missing_package_file": "<p>Fichier de paquetage du projet introuvable.</p><p>Il manque au projet le fichier <code>package.json</code>.</p>", | ||||
|       "project_empty": "<p>Le projet est vide.</p><p>Voulez-vous créer un ensemble de fichiers de projet par défaut ?<br/>Sinon, vous devrez ajouter manuellement des fichiers au projet (en dehors de l'éditeur).</p>", | ||||
|       "project_not_found": "<p>Le projet '__project__' est introuvable.</p>", | ||||
|       "git_merge_conflict": "<p>La fusion automatique des modifications a échoué.</p><p>Corriger les conflits non fusionnés, puis valider le résultat.</p>" | ||||
| @@ -219,7 +220,7 @@ | ||||
|     }, | ||||
|     "project": { | ||||
|       "change-branch": "Changer pour une branche locale '__project__'", | ||||
|       "merge-abort": "Git fusion abandonnée", | ||||
|       "merge-abort": "Fusion Git abandonnée", | ||||
|       "loaded": "Projet '__project__' chargé", | ||||
|       "updated": "Projet '__project__' mis à jour", | ||||
|       "pull": "Projet '__project__' rechargé", | ||||
| @@ -352,7 +353,7 @@ | ||||
|       "backgroundUpdate": "Les flux sur le serveur ont été mis à jour.", | ||||
|       "conflictChecking": "Vérifier si les modifications peuvent être fusionnées automatiquement", | ||||
|       "conflictAutoMerge": "Les modifications n'incluent aucun conflit et peuvent être fusionnées automatiquement.", | ||||
|       "conflictManualMerge": "Les changements incluent des conflits qui doivent être résolus avant de pouvoir être déployés.", | ||||
|       "conflictManualMerge": "Les modifications incluent des conflits qui doivent être résolus avant de pouvoir être déployées.", | ||||
|       "plusNMore": "+ __count__ en plus" | ||||
|     } | ||||
|   }, | ||||
| @@ -372,16 +373,17 @@ | ||||
|       "deleted": "supprimé", | ||||
|       "flowDeleted": "flux supprimé", | ||||
|       "flowAdded": "flux ajouté", | ||||
|       "moved": "déplacé", | ||||
|       "movedTo": "déplacé vers __id__", | ||||
|       "movedFrom": "déplacé depuis __id__" | ||||
|     }, | ||||
|     "nodeCount": "__count__ noeud", | ||||
|     "nodeCount_plural": "__count__ noeuds", | ||||
|     "local": "Changements locaux", | ||||
|     "remote": "Modifications à distance", | ||||
|     "remote": "Changements distants", | ||||
|     "reviewChanges": "Examiner les modifications", | ||||
|     "noBinaryFileShowed": "Impossible d'afficher le contenu du fichier binaire", | ||||
|     "viewCommitDiff": "Afficher les modifications de validation", | ||||
|     "viewCommitDiff": "Afficher les modifications de la validation", | ||||
|     "compareChanges": "Comparer les modifications", | ||||
|     "saveConflict": "Enregistrer la résolution des conflits", | ||||
|     "conflictHeader": "<span>__resolved__</span> sur <span>__unresolved__</span> conflit(s) résolu(s)", | ||||
| @@ -395,9 +397,9 @@ | ||||
|     "edit": "Modifier le modèle du sous-flux", | ||||
|     "subflowInstances": "Il existe __count__ instance de ce modèle de sous-flux", | ||||
|     "subflowInstances_plural": "Il existe __count__ instances de ce modèle de sous-flux", | ||||
|     "editSubflowProperties": "modifier les propriétés", | ||||
|     "input": "Entrées:", | ||||
|     "output": "Sorties:", | ||||
|     "editSubflowProperties": "Modifier les propriétés", | ||||
|     "input": "Entrées :", | ||||
|     "output": "Sorties :", | ||||
|     "status": "Statut du noeud", | ||||
|     "deleteSubflow": "Supprimer le sous-flux", | ||||
|     "confirmDelete": "Voulez-vous vraiment supprimer ce sous-flux ?", | ||||
| @@ -411,7 +413,7 @@ | ||||
|     "version": "Version", | ||||
|     "versionPlaceholder": "x.y.z", | ||||
|     "keys": "Mots clés", | ||||
|     "keysPlaceholder": "Mots clés séparés par des virgules", | ||||
|     "keysPlaceholder": "Mots clés séparés par une virgule", | ||||
|     "author": "Auteur", | ||||
|     "authorPlaceholder": "Votre nom <email@exemple.com>", | ||||
|     "desc": "Description", | ||||
| @@ -468,7 +470,7 @@ | ||||
|       "select": "sélection", | ||||
|       "checkbox": "case à cocher", | ||||
|       "spinner": "valeurs à défiler", | ||||
|       "none": "aucune", | ||||
|       "none": "aucun", | ||||
|       "hidden": "masquer la propriété" | ||||
|     }, | ||||
|     "types": { | ||||
| @@ -496,7 +498,7 @@ | ||||
|       "max": "Maximum" | ||||
|     }, | ||||
|     "errors": { | ||||
|       "scopeChange": "La modification de la portée la rendra indisponible pour les noeuds d'autres flux qui l'utilisent", | ||||
|       "scopeChange": "La modification de la portée rendra indisponible ce noeud de configuration aux noeuds d'autres flux qui l'utilisent", | ||||
|       "invalidProperties": "Propriétés invalides :", | ||||
|       "credentialLoadFailed": "Échec du chargement des identifiants du noeud" | ||||
|     } | ||||
| @@ -510,7 +512,7 @@ | ||||
|     "unassigned": "Non attribué", | ||||
|     "global": "Global", | ||||
|     "workspace": "Espace de travail", | ||||
|     "editor": "Boîte de dialogue d'édition", | ||||
|     "editor": "Boîte d'édition", | ||||
|     "selectAll": "Tout sélectionner", | ||||
|     "selectNone": "Ne rien sélectionner", | ||||
|     "selectAllConnected": "Sélectionner tous les éléments connectés", | ||||
| @@ -541,7 +543,7 @@ | ||||
|     "openLibrary": "Ouvrir la bibliothèque...", | ||||
|     "saveToLibrary": "Enregistrer dans la bibliothèque...", | ||||
|     "typeLibrary": "__type__ bibliothèque", | ||||
|     "unnamedType": "Innomé __type__", | ||||
|     "unnamedType": "Sans nom __type__", | ||||
|     "exportedToLibrary": "Noeuds exportés vers la bibliothèque", | ||||
|     "dialogSaveOverwrite": "Une __libraryType__ appelée __libraryName__ existe déjà. Écraser ?", | ||||
|     "invalidFilename": "Nom de fichier non valide", | ||||
| @@ -558,7 +560,7 @@ | ||||
|     "noInfo": "Pas d'information disponible", | ||||
|     "filter": "Rechercher le noeud", | ||||
|     "search": "Rechercher les modules", | ||||
|     "addCategory": "Ajouter un nouveau...", | ||||
|     "addCategory": "Ajouter une nouvelle...", | ||||
|     "label": { | ||||
|       "subflows": "Sous-flux", | ||||
|       "network": "Réseau", | ||||
| @@ -638,7 +640,7 @@ | ||||
|       "sortAZ": "A-Z", | ||||
|       "sortRecent": "Récent", | ||||
|       "more": "+ __count__ en plus", | ||||
|       "upload": "Charger le fichier tgz du module", | ||||
|       "upload": "Charger le fichier .tgz du module", | ||||
|       "refresh": "Actualiser la liste des modules", | ||||
|       "errors": { | ||||
|         "catalogLoadFailed": "<p>Échec du chargement du catalogue de noeuds.</p><p>Vérifier la console du navigateur pour plus d'informations</p>", | ||||
| @@ -651,7 +653,7 @@ | ||||
|       }, | ||||
|       "confirm": { | ||||
|         "install": { | ||||
|           "body": "<p>Installation de '__module__'</p><p>Avant l'installation, veuiller lire la documentation du noeud. Certains noeuds ont des dépendances qui ne peuvent pas être résolues automatiquement et peuvent nécessiter un redémarrage de Node-RED.</p>", | ||||
|           "body": "<p>Installation de '__module__'</p><p>Avant l'installation, veuillez lire la documentation du noeud. Certains noeuds ont des dépendances qui ne peuvent pas être résolues automatiquement et peuvent nécessiter un redémarrage de Node-RED.</p>", | ||||
|           "title": "Installer les noeuds" | ||||
|         }, | ||||
|         "remove": { | ||||
| @@ -666,7 +668,7 @@ | ||||
|           "title": "Mettre à jour les noeuds" | ||||
|         }, | ||||
|         "cannotUpdate": { | ||||
|           "body": "Une mise à jour pour ce noeud est disponible, mais il n'est pas installé dans un emplacement que le gestionnaire de palette peut mettre à jour.<br/><br/>Veuiller vous référer à la documentation pour savoir comment mettre à jour ce noeud." | ||||
|           "body": "Une mise à jour pour ce noeud est disponible, mais il n'est pas installé dans un emplacement que le gestionnaire de palette peut mettre à jour.<br/><br/>Veuillez vous référer à la documentation pour savoir comment mettre à jour ce noeud." | ||||
|         }, | ||||
|         "button": { | ||||
|           "review": "Ouvrir la documentation", | ||||
| @@ -708,8 +710,8 @@ | ||||
|       "nodeHelp": "Aide sur les noeuds", | ||||
|       "none": "Aucun", | ||||
|       "arrayItems": "__count__ éléments", | ||||
|       "showTips": "Vous pouvez ouvrir les astuces à partir du panneau des paramètres", | ||||
|       "outline": "Plan", | ||||
|       "showTips": "Vous pouvez afficher les astuces à partir du panneau des paramètres", | ||||
|       "outline": "Contour", | ||||
|       "empty": "Vide", | ||||
|       "globalConfig": "Noeuds de configuration globale", | ||||
|       "triggerAction": "Déclencher une action", | ||||
| @@ -722,7 +724,7 @@ | ||||
|     "help": { | ||||
|       "name": "Aide", | ||||
|       "label": "Aide", | ||||
|       "search": "Aide à la recherche", | ||||
|       "search": "Rechercher l'aide", | ||||
|       "nodeHelp": "Aide sur les noeuds", | ||||
|       "showHelp": "Afficher l'aide", | ||||
|       "showInOutline": "Afficher dans les grandes lignes", | ||||
| @@ -801,7 +803,7 @@ | ||||
|         "branches": "Branches", | ||||
|         "noBranches": "Pas de branche", | ||||
|         "deleteConfirm": "Êtes-vous sûr de vouloir supprimer la branche locale '__name__' ? Ça ne peut pas être annulé.", | ||||
|         "unmergedConfirm": "La branche locale '__name__' contient des modifications non fusionnées qui seront perdues. Etes-vous sûr de vouloir la supprimer?", | ||||
|         "unmergedConfirm": "La branche locale '__name__' contient des modifications non fusionnées qui seront perdues. Êtes-vous sûr de vouloir la supprimer?", | ||||
|         "deleteUnmergedBranch": "Supprimer la branche non fusionnée", | ||||
|         "gitRemotes": "Git distant", | ||||
|         "addRemote": "Ajout distant", | ||||
| @@ -845,17 +847,17 @@ | ||||
|         "deleteConfirm": "Êtes-vous sûr de vouloir supprimer la clé SSH __name__ ? Ça ne peut pas être annulé." | ||||
|       }, | ||||
|       "versionControl": { | ||||
|         "unstagedChanges": "Abandon des changements", | ||||
|         "stagedChanges": "Changement mis en place", | ||||
|         "unstageChange": "Ne pas mettre en place le changement", | ||||
|         "stageChange": "Mettre en place le changement", | ||||
|         "unstageAllChange": "Ne pas mettre en place tous les changements", | ||||
|         "stageAllChange": "Mettre en place tous les changements", | ||||
|         "unstagedChanges": "Changements non indexés", | ||||
|         "stagedChanges": "Changements indexés", | ||||
|         "unstageChange": "Annuler l'indexation des changements", | ||||
|         "stageChange": "Indexer les changements", | ||||
|         "unstageAllChange": "Annuler l'indexation de tous les changements", | ||||
|         "stageAllChange": "Indexer tous les changements", | ||||
|         "commitChanges": "Valider les changements", | ||||
|         "resolveConflicts": "Résoudre les conflits", | ||||
|         "head": "En-tête", | ||||
|         "staged": "Mis en place", | ||||
|         "unstaged": "Non mis en place", | ||||
|         "staged": "Indexé", | ||||
|         "unstaged": "Non indexé", | ||||
|         "local": "Local", | ||||
|         "remote": "Distant", | ||||
|         "revert": "Voulez-vous vraiment annuler les modifications apportées à '__file__' ? Ça ne peut pas être annulé.", | ||||
| @@ -889,11 +891,11 @@ | ||||
|         "pushFailed": "L'envoi a échoué car la branche a des validations plus récentes. Tirer et fusionner d'abord, puis envoyer à nouveau.", | ||||
|         "push": "Envoyer", | ||||
|         "pull": "Tirer", | ||||
|         "unablePull": "<p>Impossible d'extraire les modifications à distance ; vos modifications locales non mises en place seraient écrasées.</p><p>Valider vos modifications et réessayer.</p>", | ||||
|         "showUnstagedChanges": "Afficher les modifications non mise en place", | ||||
|         "unablePull": "<p>Impossible d'extraire les modifications à distance; vos modifications locales non mises en place seraient écrasées.</p><p>Valider vos modifications et réessayer.</p>", | ||||
|         "showUnstagedChanges": "Afficher les modifications non indexées", | ||||
|         "connectionFailed": "Impossible de se connecter au référentiel distant: ", | ||||
|         "pullUnrelatedHistory": "<p>Le réferentiel distant a un historique de validations sans rapport.</p><p>Êtes-vous sûr de vouloir extraire les modifications dans votre référentiel local ?</p>", | ||||
|         "pullChanges": "Tirer les changements", | ||||
|         "pullChanges": "Tirer les changements distants", | ||||
|         "history": "Historique", | ||||
|         "projectHistory": "Historique du projet", | ||||
|         "daysAgo": "il y a __count__ jour", | ||||
| @@ -974,7 +976,7 @@ | ||||
|     "result": "Résultat", | ||||
|     "format": "Format", | ||||
|     "compatMode": "Mode de compatibilité activé", | ||||
|     "compatModeDesc": "<h3>Mode de compatibilité JSONata</h3><p> L'expression actuelle semble toujours faire référence à <code>msg</code> et sera donc évaluée en mode de compatibilité. Veuiller mettre à jour l'expression pour ne pas utiliser <code>msg</code> car ce mode sera supprimé à l'avenir.</p><p> Lorsque la prise en charge de JSONata a été ajoutée pour la première fois à Node-RED, il fallait que l'expression référencie l'objet <code>msg</code>. Par exemple, <code>msg.payload</code> serait utilisé pour accéder à la charge utile.</p><p> Cela n'est plus nécessaire car l'expression sera évaluée directement par rapport au message. Pour accéder à la charge utile, l'expression doit être simplement <code>charge utile</code>.</p>", | ||||
|     "compatModeDesc": "<h3>Mode de compatibilité JSONata</h3><p> L'expression actuelle semble toujours faire référence à <code>msg</code> et sera donc évaluée en mode de compatibilité. Veuillez mettre à jour l'expression pour ne pas utiliser <code>msg</code> car ce mode sera supprimé à l'avenir.</p><p> Lorsque la prise en charge de JSONata a été ajoutée pour la première fois à Node-RED, il fallait que l'expression référencie l'objet <code>msg</code>. Par exemple, <code>msg.payload</code> serait utilisé pour accéder à la charge utile.</p><p> Cela n'est plus nécessaire car l'expression sera évaluée directement par rapport au message. Pour accéder à la charge utile, l'expression doit être simplement <code>charge utile</code>.</p>", | ||||
|     "noMatch": "Aucun résultat correspondant", | ||||
|     "errors": { | ||||
|       "invalid-expr": "Expression JSONata non valide :\n  __message__", | ||||
| @@ -997,7 +999,7 @@ | ||||
|   }, | ||||
|   "jsonEditor": { | ||||
|     "title": "Éditeur JSON", | ||||
|     "format": "Format JSON", | ||||
|     "format": "Formatter JSON", | ||||
|     "rawMode": "Modifier JSON", | ||||
|     "uiMode": "Afficher l'éditeur", | ||||
|     "rawMode-readonly": "JSON", | ||||
| @@ -1016,7 +1018,7 @@ | ||||
|   "markdownEditor": { | ||||
|     "title": "Éditeur Markdown", | ||||
|     "expand": "Développer", | ||||
|     "format": "Formaté avec Markdown", | ||||
|     "format": "Formatter avec Markdown", | ||||
|     "heading1": "Rubrique 1", | ||||
|     "heading2": "Rubrique 2", | ||||
|     "heading3": "Rubrique 3", | ||||
| @@ -1090,7 +1092,7 @@ | ||||
|       "credential-key": "Clé de chiffrement des identifiants", | ||||
|       "cant-get-ssh-key": "Erreur! Impossible d'obtenir le chemin de la clé SSH sélectionnée.", | ||||
|       "already-exists2": "Existe déjà", | ||||
|       "git-error": "Erreur git", | ||||
|       "git-error": "Erreur Git", | ||||
|       "connection-failed": "La connexion a échoué", | ||||
|       "not-git-repo": "Ce n'est pas un dépôt Git", | ||||
|       "repo-not-found": "Référentiel introuvable" | ||||
| @@ -1104,7 +1106,7 @@ | ||||
|       "credentials-file": "Fichier d'identifiants" | ||||
|     }, | ||||
|     "encryption-config": { | ||||
|       "setup": "Configuration du chiffrage de votre fichier d'informations d'identification", | ||||
|       "setup": "Configuration du chiffrement de votre fichier d'informations d'identification", | ||||
|       "desc0": "Votre fichier d'informations d'identification de flux peut être chiffré pour sécuriser son contenu.", | ||||
|       "desc1": "Si vous souhaitez stocker ces identifiants dans un référentiel Git public, vous devez les chiffrer en fournissant une phrase clé secrète.", | ||||
|       "desc2": "Votre fichier d'identifiants de flux n'est actuellement pas chiffré.", | ||||
| @@ -1161,9 +1163,9 @@ | ||||
|       "add-ssh-key": "Ajouter une clé ssh", | ||||
|       "credentials-encryption-key": "Clé de chiffrement des identifiants", | ||||
|       "already-exists-2": "Existe déjà", | ||||
|       "git-error": "Erreur git", | ||||
|       "git-error": "Erreur Git", | ||||
|       "con-failed": "La connexion a échoué", | ||||
|       "not-git": "Ce n'est pas un dépôt git", | ||||
|       "not-git": "Ce n'est pas un dépôt Git", | ||||
|       "no-resource": "Référentiel introuvable", | ||||
|       "cant-get-ssh-key-path": "Erreur! Impossible d'obtenir le chemin de la clé SSH sélectionnée.", | ||||
|       "unexpected_error": "Erreur inattendue", | ||||
| @@ -1201,7 +1203,7 @@ | ||||
|     }, | ||||
|     "errors": { | ||||
|       "no-username-email": "Votre client Git n'est pas configuré avec un nom d'utilisateur/e-mail.", | ||||
|       "unexpected": "Une erreur inattendue est apparue", | ||||
|       "unexpected": "Une erreur inattendue est survenue", | ||||
|       "code": "Code" | ||||
|     } | ||||
|   }, | ||||
| @@ -1270,7 +1272,7 @@ | ||||
|     "list-modified-nodes": "Afficher les flux modifiés", | ||||
|     "list-hidden-flows": "Afficher les flux cachés", | ||||
|     "list-flows": "Lister les flux", | ||||
|     "list-subflows": "Liste les sous-flux", | ||||
|     "list-subflows": "Lister les sous-flux", | ||||
|     "go-to-previous-location": "Aller à l'emplacement précédent", | ||||
|     "go-to-next-location": "Aller à l'emplacement suivant", | ||||
|     "copy-selection-to-internal-clipboard": "Copier la sélection dans le presse-papiers", | ||||
| @@ -1330,8 +1332,8 @@ | ||||
|     "align-selection-to-bottom": "Aligner la sélection vers le bas", | ||||
|     "align-selection-to-middle": "Aligner la sélection au centre verticalement", | ||||
|     "align-selection-to-center": "Aligner la sélection au centre horizontalement", | ||||
|     "distribute-selection-horizontally": "Distribuer la sélection horizontalement", | ||||
|     "distribute-selection-vertical": "Distribuer la sélection verticalement", | ||||
|     "distribute-selection-horizontally": "Répartir la sélection horizontalement", | ||||
|     "distribute-selection-vertical": "Répartir la sélection verticalement", | ||||
|     "wire-series-of-nodes": "Connecter les noeuds en série", | ||||
|     "wire-node-to-multiple": "Connecter les noeuds à plusieurs", | ||||
|     "wire-multiple-to-node": "Connecter plusieurs au noeud", | ||||
|   | ||||
| @@ -27,7 +27,8 @@ | ||||
|             "lock": "固定", | ||||
|             "unlock": "固定を解除", | ||||
|             "locked": "固定済み", | ||||
|             "unlocked": "固定なし" | ||||
|             "unlocked": "固定なし", | ||||
|             "format": "形式" | ||||
|         }, | ||||
|         "type": { | ||||
|             "string": "文字列", | ||||
| @@ -281,8 +282,8 @@ | ||||
|             "selected": "選択したフロー", | ||||
|             "current": "現在のタブ", | ||||
|             "all": "全てのタブ", | ||||
|             "compact": "インデントのないJSONフォーマット", | ||||
|             "formatted": "インデント付きのJSONフォーマット", | ||||
|             "compact": "インデントなし", | ||||
|             "formatted": "インデント付き", | ||||
|             "copy": "書き出し", | ||||
|             "export": "ライブラリに書き出し", | ||||
|             "exportAs": "書き出し先", | ||||
| @@ -923,6 +924,8 @@ | ||||
|         } | ||||
|     }, | ||||
|     "typedInput": { | ||||
|         "selected": "__count__個を選択", | ||||
|         "selected_plural": "__count__個を選択", | ||||
|         "type": { | ||||
|             "str": "文字列", | ||||
|             "num": "数値", | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@node-red/editor-client", | ||||
|     "version": "4.0.0", | ||||
|     "version": "4.1.0-beta.0", | ||||
|     "license": "Apache-2.0", | ||||
|     "repository": { | ||||
|         "type": "git", | ||||
|   | ||||
| @@ -32,24 +32,28 @@ RED.contextMenu = (function () { | ||||
|             const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g | ||||
|             let hasGroup, isAllGroups = true, hasDisabledNode, hasEnabledNode, hasLabeledNode, hasUnlabeledNode; | ||||
|             if (hasSelection) { | ||||
|                 selection.nodes.forEach(n => { | ||||
|                 const nodes = selection.nodes.slice(); | ||||
|                 while (nodes.length) { | ||||
|                     const n = nodes.shift(); | ||||
|                     if (n.type === 'group') { | ||||
|                         hasGroup = true; | ||||
|                         nodes.push(...n.nodes); | ||||
|                     } else { | ||||
|                         isAllGroups = false; | ||||
|                     } | ||||
|                         if (n.d) { | ||||
|                             hasDisabledNode = true; | ||||
|                         } else { | ||||
|                             hasEnabledNode = true; | ||||
|                         } | ||||
|                     } | ||||
|                     if (n.l === undefined || n.l) { | ||||
|                         hasLabeledNode = true; | ||||
|                     } else { | ||||
|                         hasUnlabeledNode = true; | ||||
|                     } | ||||
|                 }); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             const offset = $("#red-ui-workspace-chart").offset() | ||||
|  | ||||
|             let addX = options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft() | ||||
|   | ||||
| @@ -157,6 +157,12 @@ RED.editor = (function() { | ||||
|             } | ||||
|         } | ||||
|         if (valid && "validate" in definition[property]) { | ||||
|             if (definition[property].hasOwnProperty("required") && | ||||
|                 definition[property].required === false) { | ||||
|                 if (value === "") { | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|             try { | ||||
|                 var opt = {}; | ||||
|                 if (label) { | ||||
| @@ -183,6 +189,11 @@ RED.editor = (function() { | ||||
|                 }); | ||||
|             } | ||||
|         } else if (valid) { | ||||
|             if (definition[property].hasOwnProperty("required") && definition[property].required === false) { | ||||
|                 if (value === "") { | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|             // If the validator is not provided in node property => Check if the input has a validator | ||||
|             if ("category" in node._def) { | ||||
|                 const isConfig = node._def.category === "config"; | ||||
| @@ -413,11 +424,8 @@ RED.editor = (function() { | ||||
|             if (selectedOpt?.data('env')) { | ||||
|                 disableButton(addButton, true); | ||||
|                 disableButton(editButton, true); | ||||
|             // disable the edit button if no options available | ||||
|             } else if (optionsLength === 1 && selectedOpt.val() === "_ADD_") { | ||||
|                 disableButton(addButton, false); | ||||
|                 disableButton(editButton, true); | ||||
|             } else if (selectedOpt.val() === "") { | ||||
|             // disable the edit button if no options available or 'none' selected | ||||
|             } else if (optionsLength === 1 || selectedOpt.val() === "_ADD_") { | ||||
|                 disableButton(addButton, false); | ||||
|                 disableButton(editButton, true); | ||||
|             } else { | ||||
| @@ -426,14 +434,9 @@ RED.editor = (function() { | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         var label = ""; | ||||
|         var configNode = RED.nodes.node(nodeValue); | ||||
|  | ||||
|         if (configNode) { | ||||
|             label = RED.utils.getNodeLabel(configNode, configNode.id); | ||||
|         } | ||||
|  | ||||
|         input.val(label); | ||||
|         // If the value is "", 'add new...' option if no config node available or 'none' option | ||||
|         // Otherwise, it's a config node | ||||
|         select.val(nodeValue || '_ADD_'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -934,9 +937,11 @@ RED.editor = (function() { | ||||
|                 } | ||||
|  | ||||
|                 if (!configNodes.length) { | ||||
|                     // Add 'add new...' option | ||||
|                     select.append('<option value="_ADD_" selected>' + RED._("editor.addNewType", { type: label }) + '</option>'); | ||||
|                 } else { | ||||
|                     select.append('<option value="">' + RED._("editor.inputs.none") + '</option>'); | ||||
|                     // Add 'none' option | ||||
|                     select.append('<option value="_ADD_">' + RED._("editor.inputs.none") + '</option>'); | ||||
|                 } | ||||
|  | ||||
|                 window.setTimeout(function() { select.trigger("change");},50); | ||||
|   | ||||
| @@ -165,7 +165,13 @@ RED.editor.codeEditor.monaco = (function() { | ||||
|         //Handles orphaned models | ||||
|         //ensure loaded models that are not explicitly destroyed by a call to .destroy() are disposed | ||||
|         RED.events.on("editor:close",function() { | ||||
|             let models = window.monaco ? monaco.editor.getModels() : null; | ||||
|             if (!window.monaco) { return; } | ||||
|             const editors = window.monaco.editor.getEditors() | ||||
|             const orphanEditors = editors.filter(editor => editor && !document.body.contains(editor.getDomNode())) | ||||
|             orphanEditors.forEach(editor => { | ||||
|                 editor.dispose(); | ||||
|             }); | ||||
|             let models = monaco.editor.getModels() | ||||
|             if(models && models.length) { | ||||
|                 console.warn("Cleaning up monaco models left behind. Any node that calls createEditor() should call .destroy().") | ||||
|                 for (let index = 0; index < models.length; index++) { | ||||
| @@ -1124,6 +1130,7 @@ RED.editor.codeEditor.monaco = (function() { | ||||
|  | ||||
|             $(el).remove(); | ||||
|             $(toolbarRow).remove(); | ||||
|             ed.dispose(); | ||||
|         } | ||||
|  | ||||
|         ed.resize = function resize() { | ||||
|   | ||||
| @@ -11,9 +11,22 @@ RED.editor.mermaid = (function () { | ||||
|              | ||||
|             if (!initializing) { | ||||
|                 initializing = true | ||||
|                 $.getScript( | ||||
|                     'vendor/mermaid/mermaid.min.js', | ||||
|                     function (data, stat, jqxhr) { | ||||
|                 // Find the cache-buster: | ||||
|                 let cacheBuster | ||||
|                 $('script').each(function (i, el) {  | ||||
|                     if (!cacheBuster) { | ||||
|                         const src = el.getAttribute('src') | ||||
|                         const m = /\?v=(.+)$/.exec(src) | ||||
|                         if (m) { | ||||
|                             cacheBuster = m[1] | ||||
|                         } | ||||
|                     } | ||||
|                 }) | ||||
|                 $.ajax({ | ||||
|                     url: `vendor/mermaid/mermaid.min.js?v=${cacheBuster}`, | ||||
|                     dataType: "script", | ||||
|                     cache: true, | ||||
|                     success: function (data, stat, jqxhr) { | ||||
|                         mermaid.initialize({ | ||||
|                             startOnLoad: false, | ||||
|                             theme: RED.settings.get('mermaid', {}).theme | ||||
| @@ -24,7 +37,7 @@ RED.editor.mermaid = (function () { | ||||
|                             render(pending) | ||||
|                         } | ||||
|                     } | ||||
|                 ) | ||||
|                 }); | ||||
|             } | ||||
|         } else { | ||||
|             const nodes = document.querySelectorAll(selector) | ||||
|   | ||||
| @@ -1100,7 +1100,7 @@ RED.subflow = (function() { | ||||
|                 input.val(val.value); | ||||
|                 break; | ||||
|             case "cred": | ||||
|                 input = $('<input type="password">').css('width','70%').appendTo(row); | ||||
|                 input = $('<input type="password">').css('width','70%').attr('id', elId).appendTo(row); | ||||
|                 if (node.credentials) { | ||||
|                     if (node.credentials[tenv.name]) { | ||||
|                         input.val(node.credentials[tenv.name]); | ||||
| @@ -1346,7 +1346,7 @@ RED.subflow = (function() { | ||||
|                         } | ||||
|                         break; | ||||
|                     case "cred": | ||||
|                         item.value = input.val(); | ||||
|                         item.value = input.typedInput('value'); | ||||
|                         item.type = 'cred'; | ||||
|                         break; | ||||
|                     case "spinner": | ||||
|   | ||||
| @@ -103,7 +103,7 @@ RED.sidebar.info.outliner = (function() { | ||||
|                 evt.stopPropagation(); | ||||
|                 RED.search.show("type:subflow:"+n.id); | ||||
|             }) | ||||
|             // RED.popover.tooltip(userCountBadge,function() { return RED._('editor.nodesUse',{count:n.users.length})}); | ||||
|             RED.popover.tooltip(subflowInstanceBadge,function() { return RED._('subflow.subflowInstances',{count:n.instances.length})}); | ||||
|         } | ||||
|         if (n._def.category === "config" && n.type !== "group") { | ||||
|             var userCountBadge = $('<button type="button" class="red-ui-info-outline-item-control-users red-ui-button red-ui-button-small"><i class="fa fa-toggle-right"></i></button>').text(n.users.length).appendTo(controls).on("click",function(evt) { | ||||
|   | ||||
| @@ -259,7 +259,7 @@ $deploy-button-background-disabled-hover: #555; | ||||
|  | ||||
| $header-background: #000; | ||||
| $header-button-background-active: #121212; | ||||
| $header-accent: #d41313; | ||||
| $header-accent: #C02020; | ||||
| $header-menu-color: #eee; | ||||
| $header-menu-color-disabled: #666; | ||||
| $header-menu-heading-color: #fff; | ||||
|   | ||||
| @@ -108,12 +108,13 @@ in your Node-RED user directory (${RED.settings.userDir}). | ||||
|             if (n.proxy && proxyConfig) { | ||||
|                 proxyOptions.env = { | ||||
|                     no_proxy: (proxyConfig.noproxy || []).join(','), | ||||
|                     http_proxy: (proxyConfig.url) | ||||
|                     http_proxy: (proxyConfig.url), | ||||
|                     https_proxy: (proxyConfig.url) | ||||
|                 } | ||||
|             } | ||||
|             return getProxyForUrl(url, proxyOptions) | ||||
|         } | ||||
|         let prox = getProxy(nodeUrl || '') | ||||
|         let prox = nodeUrl ? getProxy(nodeUrl) : null | ||||
|  | ||||
|         let timingLog = false; | ||||
|         if (RED.settings.hasOwnProperty("httpRequestTimingLog")) { | ||||
| @@ -534,9 +535,7 @@ in your Node-RED user directory (${RED.settings.userDir}). | ||||
|                 opts.headers[clSet] = opts.headers['content-length']; | ||||
|                 delete opts.headers['content-length']; | ||||
|             } | ||||
|             if (!opts.headers.hasOwnProperty('user-agent')) { | ||||
|                 opts.headers['user-agent'] = 'Mozilla/5.0 (Node-RED)'; | ||||
|             } | ||||
|  | ||||
|             if (proxyUrl) { | ||||
|                 const match = proxyUrl.match(/^(https?:\/\/)?(.+)?:([0-9]+)?/i); | ||||
|                 if (match) { | ||||
| @@ -566,7 +565,7 @@ in your Node-RED user directory (${RED.settings.userDir}). | ||||
|                     //need both incase of http -> https redirect | ||||
|                     opts.agent = { | ||||
|                         http: new HttpProxyAgent(proxyOptions), | ||||
|                         https: new HttpProxyAgent(proxyOptions) | ||||
|                         https: new HttpsProxyAgent(proxyOptions) | ||||
|                     }; | ||||
|  | ||||
|                 } else { | ||||
|   | ||||
| @@ -17,7 +17,11 @@ | ||||
| <script type="text/html" data-template-name="split"> | ||||
|     <!-- <div class="form-row"><span data-i18n="[html]split.intro"></span></div> --> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-property"><i class="fa fa-forward"></i> <span data-i18n="split.split"></span></label> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name"> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-property"><i class="fa fa-forward"></i> <span data-i18n="split.splitThe"></span></label> | ||||
|         <input type="text" id="node-input-property" style="width:70%;"> | ||||
|     </div> | ||||
|     <div class="form-row"><span data-i18n="[html]split.strBuff"></span></div> | ||||
| @@ -43,10 +47,6 @@ | ||||
|         <label for="node-input-addname-cb" style="width:auto;" data-i18n="split.addname"></label> | ||||
|         <input type="text" id="node-input-addname" style="width:70%"> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name"> | ||||
|     </div> | ||||
| </script> | ||||
|  | ||||
| <script type="text/javascript"> | ||||
| @@ -122,6 +122,10 @@ | ||||
|  | ||||
|  | ||||
| <script type="text/html" data-template-name="join"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label data-i18n="join.mode.mode"></label> | ||||
|         <select id="node-input-mode" style="width:200px;"> | ||||
| @@ -157,6 +161,12 @@ | ||||
|             <input type="text" id="node-input-joiner" style="width:70%"> | ||||
|             <input type="hidden" id="node-input-joinerType"> | ||||
|         </div> | ||||
|  | ||||
|         <div class="form-row"> | ||||
|             <input type="checkbox" id="node-input-useparts" style="margin-left:8px; margin-right:8px; vertical-align:baseline; width:auto;"> | ||||
|             <label for="node-input-useparts" style="width:auto;" data-i18n="join.useparts"></label> | ||||
|         </div> | ||||
|  | ||||
|         <div class="form-row node-row-trigger" id="trigger-row"> | ||||
|             <label style="width:auto;" data-i18n="join.send"></label> | ||||
|             <ul> | ||||
| @@ -195,10 +205,6 @@ | ||||
|             <label for="node-input-reduceRight" data-i18n="join.reduce.right" style="width:70%; margin-left:10px;"></label> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> | ||||
|     </div> | ||||
|     <div class="form-tips form-tips-auto hide" data-i18n="[html]join.tip"></div> | ||||
| </script> | ||||
|  | ||||
| @@ -234,6 +240,7 @@ | ||||
|             }, | ||||
|             joiner: { value:"\\n"}, | ||||
|             joinerType: { value:"str"}, | ||||
|             useparts: { value:false }, | ||||
|             accumulate: { value:"false" }, | ||||
|             timeout: {value:""}, | ||||
|             count: {value:""}, | ||||
| @@ -259,6 +266,12 @@ | ||||
|         }, | ||||
|         oneditprepare: function() { | ||||
|             var node = this; | ||||
|             $("#node-input-useparts").on("change", function(e) { | ||||
|                 if (node.useparts === undefined) { | ||||
|                     node.useparts = true; | ||||
|                     $("#node-input-useparts").attr('checked', true); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             $("#node-input-mode").on("change", function(e) { | ||||
|                 var val = $(this).val(); | ||||
|   | ||||
| @@ -444,6 +444,8 @@ module.exports = function(RED) { | ||||
|         this.count = Number(n.count || 0); | ||||
|         this.joiner = n.joiner||""; | ||||
|         this.joinerType = n.joinerType||"str"; | ||||
|         if (n.useparts === undefined) { this.useparts = true; } | ||||
|         else { this.useparts = n.useparts || false; } | ||||
|  | ||||
|         this.reduce = (this.mode === "reduce"); | ||||
|         if (this.reduce) { | ||||
| @@ -611,7 +613,7 @@ module.exports = function(RED) { | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 if (node.mode === 'custom' && msg.hasOwnProperty('parts')) { | ||||
|                 if (node.mode === 'custom' && msg.hasOwnProperty('parts') && node.useparts === false ) { | ||||
|                     if (msg.parts.hasOwnProperty('parts')) { | ||||
|                         msg.parts = { parts: msg.parts.parts }; | ||||
|                     } | ||||
|   | ||||
| @@ -36,6 +36,10 @@ | ||||
|             <label style="margin-left: 10px; width: 175px;" for="node-input-overlap" data-i18n="batch.count.overlap"></label> | ||||
|             <input type="text" id="node-input-overlap" style="width: 50px;"> | ||||
|         </div> | ||||
|         <div class="form-row"> | ||||
|             <input type="checkbox" id="node-input-honourParts" style="margin-left: 10px; margin-right:10px; vertical-align:top; width:auto;"> | ||||
|             <label for="node-input-honourParts" style="width:auto;" data-i18n="batch.honourParts"></label> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="node-row-msg-interval"> | ||||
| @@ -45,7 +49,7 @@ | ||||
|             <span data-i18n="batch.interval.seconds"></span> | ||||
|         </div> | ||||
|         <div class="form-row"> | ||||
|             <input type="checkbox" id="node-input-allowEmptySequence" style="margin-left:20px; margin-right: 10px; vertical-align:top; width:auto;"> | ||||
|             <input type="checkbox" id="node-input-allowEmptySequence" style="margin-left:20px; margin-right:10px; vertical-align:top; width:auto;"> | ||||
|             <label for="node-input-allowEmptySequence" style="width:auto;" data-i18n="batch.interval.empty"></label> | ||||
|         </div> | ||||
|     </div> | ||||
| @@ -101,6 +105,7 @@ | ||||
|                 } | ||||
|             }, | ||||
|             allowEmptySequence: {value:false}, | ||||
|             honourParts: {value:false}, | ||||
|             topics: {value:[{topic:""}]} | ||||
|         }, | ||||
|         inputs:1, | ||||
|   | ||||
| @@ -181,6 +181,8 @@ module.exports = function(RED) { | ||||
|         RED.nodes.createNode(this,n); | ||||
|         var node = this; | ||||
|         var mode = n.mode || "count"; | ||||
|         var eof = false; | ||||
|         node.honourParts = n.honourParts || false; | ||||
|  | ||||
|         node.pending_count = 0; | ||||
|         if (mode === "count") { | ||||
| @@ -201,9 +203,12 @@ module.exports = function(RED) { | ||||
|                     return; | ||||
|                 } | ||||
|                 var queue = node.pending; | ||||
|                 if (node.honourParts && msg.hasOwnProperty("parts")) { | ||||
|                     if (msg.parts.index + 1 === msg.parts.count) { eof = true; } | ||||
|                 } | ||||
|                 queue.push({msg, send, done}); | ||||
|                 node.pending_count++; | ||||
|                 if (queue.length === count) { | ||||
|                 if (queue.length === count || eof === true) { | ||||
|                     send_msgs(node, queue, is_overlap); | ||||
|                     for (let i = 0; i < queue.length-overlap; i++) { | ||||
|                         queue[i].done(); | ||||
| @@ -211,6 +216,7 @@ module.exports = function(RED) { | ||||
|                     node.pending = | ||||
|                         (overlap === 0) ? [] : queue.slice(-overlap); | ||||
|                     node.pending_count = 0; | ||||
|                     eof = false; | ||||
|                 } | ||||
|                 var max_msgs = max_kept_msgs_count(node); | ||||
|                 if ((max_msgs > 0) && (node.pending_count > max_msgs)) { | ||||
|   | ||||
| @@ -20,12 +20,26 @@ | ||||
|         <dt class="optional">delay <span class="property-type">number</span></dt> | ||||
|         <dd>Legt die Verzögerung in Millisekunden fest, die auf die Nachricht angewendet werden soll. | ||||
|             Zur Nutzung dieser Option muss <i>Verzög. mit msg.delay überschreibbar</i> aktiviert sein.</dd> | ||||
|         <dt class="optional">rate <span class="property-type">number</span></dt> | ||||
|         <dd>Setzt die Verzögerung in Millisekunden zwischen den Nachrichten. Diese Node überschreibt die | ||||
|             bestehende Verzögerung die in der Node konfiguration, wenn die empfangende Nachricht <code>msg.rate</code> | ||||
|             in Millisekunden enthält. Dies trifft nur zu, wenn in der Node konfiguriert ist, das empfangene | ||||
|             Nachrichten den konfigurierten Wert überschreiben können.</dd> | ||||
|         <dt class="optional">reset</dt> | ||||
|         <dd>Wenn bei der empfangenen Nachricht diese Eigenschaft auf einen beliebigen Wert gesetzt ist, | ||||
|             werden alle im Node gepufferten Nachrichten gelöscht.</dd> | ||||
|         <dt class="optional">flush</dt> | ||||
|         <dd>Wenn bei der empfangenen Nachricht diese Eigenschaft auf einen beliebigen Wert gesetzt ist, | ||||
|             werden alle im Node gepufferten Nachrichten sofort gesendet.</dd> | ||||
|         <dt class="optional">flush</dt> | ||||
|         <dd>Wenn bei der empfangenen Nachricht diese Eigenschaft auf einen numerischen Wert gesetzt ist, | ||||
|             wird diese Anzahl an Nachrichten sofort gesendet. Wenn ein anderer Typ gesetzt ist (z.B. Boolean), | ||||
|             werden alle in der Node gepufferten Nachrichten gesendet.</dd> | ||||
|         <dt class="optional">toFront</dt> | ||||
|         <dd>Wenn diese Eigenschaft im Ratenbegrenzungsmodus für die empfangene Nachricht auf den booleschen Wert | ||||
|             <code>true</code> gesetzt ist, Anschließend wird die Nachricht an den Anfang der Warteschlange verschoben | ||||
|             und als nächstes freigegeben. Dies kann in Kombination mit <code>msg.flush=1</code> verwendet werden, um sofort erneut zu senden. | ||||
|         </dd> | ||||
|     </dl> | ||||
|     <h3>Details</h3> | ||||
|     <p>Wenn Verzögerung als Nachrichtenaktion eingestellt ist, kann die Verzögerungszeit ein fixer Wert, | ||||
|   | ||||
| @@ -912,6 +912,7 @@ | ||||
|         "objectSend": "Sende eine Nachricht für jedes Schlüssel/Wert-Paar", | ||||
|         "strBuff": "<b>string</b> / <b>buffer</b>", | ||||
|         "array": "<b>array</b>", | ||||
|         "splitThe": "Split", | ||||
|         "splitUsing": "Aufteilung", | ||||
|         "splitLength": "feste Längen von", | ||||
|         "stream": "Als Nachrichtenstrom behandeln (Streaming-Modus)", | ||||
|   | ||||
| @@ -1011,12 +1011,13 @@ | ||||
|         "tip": "Tip: The filename should be an absolute path, otherwise it will be relative to the working directory of the Node-RED process." | ||||
|     }, | ||||
|     "split": { | ||||
|         "split": "Split", | ||||
|         "split": "split", | ||||
|         "intro": "Split <code>msg.payload</code> based on type:", | ||||
|         "object": "<b>Object</b>", | ||||
|         "objectSend": "Send a message for each key/value pair", | ||||
|         "strBuff": "<b>String</b> / <b>Buffer</b>", | ||||
|         "array": "<b>Array</b>", | ||||
|         "splitThe": "Split the", | ||||
|         "splitUsing": "Split using", | ||||
|         "splitLength": "Fixed length of", | ||||
|         "stream": "Handle as a stream of messages", | ||||
| @@ -1046,6 +1047,7 @@ | ||||
|         "joinedUsing": "joined using", | ||||
|         "send": "Send the message:", | ||||
|         "afterCount": "After a number of message parts", | ||||
|         "useparts": "Use existing msg.parts property", | ||||
|         "count": "count", | ||||
|         "subsequent": "and every subsequent message.", | ||||
|         "afterTimeout": "After a timeout following the first message", | ||||
| @@ -1112,6 +1114,7 @@ | ||||
|         "too-many": "too many pending messages in batch node", | ||||
|         "unexpected": "unexpected mode", | ||||
|         "no-parts": "no parts property in message", | ||||
|         "honourParts": "Allow msg.parts to also complete batch operation.", | ||||
|         "error": { | ||||
|             "invalid-count": "Invalid count", | ||||
|             "invalid-overlap": "Invalid overlap", | ||||
|   | ||||
| @@ -1017,6 +1017,7 @@ | ||||
|         "objectSend": "各key/valueペアのメッセージを送信", | ||||
|         "strBuff": "<b>文字列</b> / <b>バッファ</b>", | ||||
|         "array": "<b>配列</b>", | ||||
|         "splitThe": "に基づく", | ||||
|         "splitUsing": "分割", | ||||
|         "splitLength": "固定長", | ||||
|         "stream": "メッセージのストリームとして処理", | ||||
|   | ||||
| @@ -994,6 +994,7 @@ | ||||
|          "objectSend": "Envia uma mensagem para cada par chave/valor", | ||||
|          "strBuff": "<b>Cadeia de caracteres</b> / <b>Armazenamento Temporário</b>", | ||||
|          "array": "<b>Matriz</b>", | ||||
|          "splitThe": "Dividir", | ||||
|          "splitUsing": "Dividir usando", | ||||
|          "splitLength": "Comprimento fixo de", | ||||
|          "stream": "Tratar como uma transmissão de mensagens", | ||||
|   | ||||
| @@ -874,6 +874,7 @@ | ||||
|         "objectSend":"Отправлять сообщение для каждой пары ключ/значение", | ||||
|         "strBuff":"<b>Строка</b> / <b>Буфер</b>", | ||||
|         "array":"<b>Массив</b>", | ||||
|         "splitThe": "Pазделить", | ||||
|         "splitUsing":"С помощью", | ||||
|         "splitLength":"Фикс. длина", | ||||
|         "stream":"Обрабатывать как поток сообщений", | ||||
|   | ||||
| @@ -997,6 +997,7 @@ | ||||
|     "objectSend": "每个键值对作为单个消息发送", | ||||
|     "strBuff": "<b>字符串</b> / <b>Buffer</b>", | ||||
|     "array": "<b>数组</b>", | ||||
|     "splitThe": "Split", | ||||
|     "splitUsing": "拆分使用", | ||||
|     "splitLength": "固定长度", | ||||
|     "stream": "作为消息流处理", | ||||
|   | ||||
| @@ -866,6 +866,7 @@ | ||||
|         "objectSend": "每個鍵值對作為單個消息發送", | ||||
|         "strBuff": "<b>字串</b> / <b>Buffer</b>", | ||||
|         "array": "<b>陣列</b>", | ||||
|         "splitThe": "Split", | ||||
|         "splitUsing": "拆分使用", | ||||
|         "splitLength": "固定長度", | ||||
|         "stream": "作為消息流處理", | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@node-red/nodes", | ||||
|     "version": "4.0.0", | ||||
|     "version": "4.1.0-beta.0", | ||||
|     "license": "Apache-2.0", | ||||
|     "repository": { | ||||
|         "type": "git", | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@node-red/registry", | ||||
|     "version": "4.0.0", | ||||
|     "version": "4.1.0-beta.0", | ||||
|     "license": "Apache-2.0", | ||||
|     "main": "./lib/index.js", | ||||
|     "repository": { | ||||
| @@ -16,7 +16,7 @@ | ||||
|         } | ||||
|     ], | ||||
|     "dependencies": { | ||||
|         "@node-red/util": "4.0.0", | ||||
|         "@node-red/util": "4.1.0-beta.0", | ||||
|         "clone": "2.1.2", | ||||
|         "fs-extra": "11.2.0", | ||||
|         "semver": "7.5.4", | ||||
|   | ||||
| @@ -645,16 +645,27 @@ function getFlow(id) { | ||||
|     if (id !== 'global') { | ||||
|         result.nodes = []; | ||||
|     } | ||||
|  | ||||
|     if (flow.groups) { | ||||
|         var nodeIds = Object.keys(flow.groups); | ||||
|         if (nodeIds.length > 0) { | ||||
|             nodeIds.forEach(function(nodeId) { | ||||
|                 var node = jsonClone(flow.groups[nodeId]); | ||||
|                 delete node.credentials; | ||||
|                 result.nodes.push(node) | ||||
|             }) | ||||
|         } | ||||
|     } | ||||
|     if (flow.nodes) { | ||||
|         var nodeIds = Object.keys(flow.nodes); | ||||
|         if (nodeIds.length > 0) { | ||||
|             result.nodes = nodeIds.map(function(nodeId) { | ||||
|             nodeIds.forEach(function(nodeId) { | ||||
|                 var node = jsonClone(flow.nodes[nodeId]); | ||||
|                 if (node.type === 'link out') { | ||||
|                     delete node.wires; | ||||
|                 } | ||||
|                 delete node.credentials; | ||||
|                 return node; | ||||
|                 result.nodes.push(node) | ||||
|             }) | ||||
|         } | ||||
|     } | ||||
| @@ -680,6 +691,17 @@ function getFlow(id) { | ||||
|                 delete node.credentials | ||||
|                 return node | ||||
|             }); | ||||
|             if (subflow.groups) { | ||||
|                 var nodeIds = Object.keys(subflow.groups); | ||||
|                 if (nodeIds.length > 0) { | ||||
|                     nodeIds.forEach(function(nodeId) { | ||||
|                         var node = jsonClone(subflow.groups[nodeId]); | ||||
|                         delete node.credentials; | ||||
|                         subflow.nodes.push(node) | ||||
|                     }) | ||||
|                 } | ||||
|                 delete subflow.groups | ||||
|             } | ||||
|             if (subflow.configs) { | ||||
|                 var configIds = Object.keys(subflow.configs); | ||||
|                 subflow.configs = configIds.map(function(id) { | ||||
|   | ||||
| @@ -23,6 +23,7 @@ module.exports = { | ||||
|             if (existingSessionId) { | ||||
|                 connections.delete(opts.session) | ||||
|                 const session = sessions.get(existingSessionId) | ||||
|                 if (session) { | ||||
|                     session.active = false | ||||
|                     session.idleTimeout = setTimeout(() => { | ||||
|                         sessions.delete(existingSessionId) | ||||
| @@ -32,6 +33,7 @@ module.exports = { | ||||
|                         data: { session: existingSessionId } | ||||
|                     }) | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         runtime.events.on('comms:message:multiplayer/connect', (opts) => { | ||||
|             let session | ||||
| @@ -91,6 +93,7 @@ module.exports = { | ||||
|             const sessionId = connections.get(opts.session) | ||||
|             const session = sessions.get(sessionId) | ||||
|  | ||||
|             if (session) { | ||||
|                 if (opts.user) { | ||||
|                     if (session.user.anonymous !== opts.user.anonymous) { | ||||
|                         session.user = opts.user | ||||
| @@ -114,6 +117,7 @@ module.exports = { | ||||
|                     data: payload, | ||||
|                     excludeSession: opts.session | ||||
|                 }) | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@node-red/runtime", | ||||
|     "version": "4.0.0", | ||||
|     "version": "4.1.0-beta.0", | ||||
|     "license": "Apache-2.0", | ||||
|     "main": "./lib/index.js", | ||||
|     "repository": { | ||||
| @@ -16,8 +16,8 @@ | ||||
|         } | ||||
|     ], | ||||
|     "dependencies": { | ||||
|         "@node-red/registry": "4.0.0", | ||||
|         "@node-red/util": "4.0.0", | ||||
|         "@node-red/registry": "4.1.0-beta.0", | ||||
|         "@node-red/util": "4.1.0-beta.0", | ||||
|         "async-mutex": "0.5.0", | ||||
|         "clone": "2.1.2", | ||||
|         "express": "4.19.2", | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "@node-red/util", | ||||
|     "version": "4.0.0", | ||||
|     "version": "4.1.0-beta.0", | ||||
|     "license": "Apache-2.0", | ||||
|     "repository": { | ||||
|         "type": "git", | ||||
|   | ||||
							
								
								
									
										10
									
								
								packages/node_modules/node-red/package.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								packages/node_modules/node-red/package.json
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|     "name": "node-red", | ||||
|     "version": "4.0.0", | ||||
|     "version": "4.1.0-beta.0", | ||||
|     "description": "Low-code programming for event-driven applications", | ||||
|     "homepage": "https://nodered.org", | ||||
|     "license": "Apache-2.0", | ||||
| @@ -31,10 +31,10 @@ | ||||
|         "flow" | ||||
|     ], | ||||
|     "dependencies": { | ||||
|         "@node-red/editor-api": "4.0.0", | ||||
|         "@node-red/runtime": "4.0.0", | ||||
|         "@node-red/util": "4.0.0", | ||||
|         "@node-red/nodes": "4.0.0", | ||||
|         "@node-red/editor-api": "4.1.0-beta.0", | ||||
|         "@node-red/runtime": "4.1.0-beta.0", | ||||
|         "@node-red/util": "4.1.0-beta.0", | ||||
|         "@node-red/nodes": "4.1.0-beta.0", | ||||
|         "basic-auth": "2.0.1", | ||||
|         "bcryptjs": "2.4.3", | ||||
|         "cors": "2.8.5", | ||||
|   | ||||
| @@ -17,6 +17,8 @@ | ||||
| var http = require("http"); | ||||
| var https = require("https"); | ||||
| var should = require("should"); | ||||
| var sinon = require("sinon"); | ||||
| var httpProxyHelper = require("nr-test-utils").require("@node-red/nodes/core/network/lib/proxyHelper.js"); | ||||
| var express = require("express"); | ||||
| var bodyParser = require('body-parser'); | ||||
| var stoppable = require('stoppable'); | ||||
| @@ -493,6 +495,7 @@ describe('HTTP Request Node', function() { | ||||
|     }); | ||||
|  | ||||
|     afterEach(function() { | ||||
|         sinon.restore(); | ||||
|         process.env.http_proxy = preEnvHttpProxyLowerCase; | ||||
|         process.env.HTTP_PROXY = preEnvHttpProxyUpperCase; | ||||
|         // On Windows, if environment variable of NO_PROXY that includes lower cases | ||||
| @@ -1799,27 +1802,80 @@ describe('HTTP Request Node', function() { | ||||
|             }) | ||||
|         }); | ||||
|  | ||||
|         //Removing HTTP Proxy testcases as GOT + Proxy_Agent doesn't work with mock'd proxy | ||||
|         /* */ | ||||
|         it('should use http_proxy', function(done) { | ||||
|             var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"POST",ret:"obj",url:getTestURL('/postInspect')}, | ||||
|                 {id:"n2", type:"helper"}]; | ||||
|         it('should use env var http_proxy', function(done) { | ||||
|             const url = getTestURL('/postInspect') | ||||
|             const proxyUrl = "http://localhost:" + testProxyPort | ||||
|              | ||||
|             const flow = [ | ||||
|                 { id: "n1", type: "http request", wires: [["n2"]], method: "POST", ret: "obj", url: url }, | ||||
|                 { id: "n2", type: "helper" }, | ||||
|             ]; | ||||
|             const proxySpy = sinon.spy(httpProxyHelper, 'getProxyForUrl') | ||||
|             const testNode = [httpRequestNode, httpProxyNode]; | ||||
|             deleteProxySetting(); | ||||
|             process.env.http_proxy = "http://localhost:" + testProxyPort; | ||||
|             helper.load(httpRequestNode, flow, function() { | ||||
|                 var n1 = helper.getNode("n1"); | ||||
|                 var n2 = helper.getNode("n2"); | ||||
|                 n2.on("input", function(msg) { | ||||
|             process.env.http_proxy = proxyUrl | ||||
|             helper.load(testNode, flow, function (msg) { | ||||
|                 try { | ||||
|                         msg.should.have.property('statusCode',200); | ||||
|                         msg.payload.should.have.property('headers'); | ||||
|                         //msg.payload.headers.should.have.property('x-testproxy-header','foobar'); | ||||
|                         done(); | ||||
|                     } catch(err) { | ||||
|                     // static URL set in the nodes configuration and the proxy will be setup upon initialisation | ||||
|                     proxySpy.calledOnce.should.be.true() | ||||
|                     proxySpy.calledWith(url, { }).should.be.true() | ||||
|                     proxySpy.returnValues[0].should.be.equal(proxyUrl) | ||||
|                     done() | ||||
|                 } catch (err) { | ||||
|                     done(err); | ||||
|                 } | ||||
|             }); | ||||
|                 n1.receive({payload:"foo"}); | ||||
|         }); | ||||
|  | ||||
|         it('should use env var https_proxy', function(done) { | ||||
|             const url = getSslTestURL('/postInspect') | ||||
|             const proxyUrl = "http://localhost:" + testProxyPort | ||||
|              | ||||
|             const flow = [ | ||||
|                 { id: "n1", type: "http request", wires: [["n2"]], method: "POST", ret: "obj", url: url }, | ||||
|                 { id: "n2", type: "helper" }, | ||||
|             ]; | ||||
|             const proxySpy = sinon.spy(httpProxyHelper, 'getProxyForUrl') | ||||
|             const testNode = [httpRequestNode, httpProxyNode]; | ||||
|             deleteProxySetting(); | ||||
|             process.env.https_proxy = proxyUrl | ||||
|             helper.load(testNode, flow, function (msg) { | ||||
|                 try { | ||||
|                     // static URL set in the nodes configuration and the proxy will be setup upon initialisation | ||||
|                     proxySpy.calledOnce.should.be.true() | ||||
|                     proxySpy.calledWith(url, { }).should.be.true() | ||||
|                     proxySpy.returnValues[0].should.be.equal(proxyUrl) | ||||
|                     done() | ||||
|                 } catch (err) { | ||||
|                     done(err); | ||||
|                 } | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it('should not use env var http*_proxy when no_proxy is set', function(done) { | ||||
|             const url = getSslTestURL('/postInspect') | ||||
|             const proxyUrl = "http://localhost:" + testProxyPort | ||||
|              | ||||
|             const flow = [ | ||||
|                 { id: "n1", type: "http request", wires: [["n2"]], method: "POST", ret: "obj", url: url }, | ||||
|                 { id: "n2", type: "helper" }, | ||||
|             ]; | ||||
|             const proxySpy = sinon.spy(httpProxyHelper, 'getProxyForUrl') | ||||
|             const testNode = [httpRequestNode, httpProxyNode]; | ||||
|             deleteProxySetting(); | ||||
|             process.env.http_proxy = proxyUrl | ||||
|             process.env.https_proxy = proxyUrl | ||||
|             process.env.no_proxy = "localhost" | ||||
|             helper.load(testNode, flow, function (msg) { | ||||
|                 try { | ||||
|                     // static URL set in the nodes configuration and the proxy will be setup upon initialisation | ||||
|                     proxySpy.calledOnce.should.be.true() | ||||
|                     proxySpy.calledWith(url, { }).should.be.true() | ||||
|                     proxySpy.returnValues[0].should.be.equal('') | ||||
|                     done() | ||||
|                 } catch (err) { | ||||
|                     done(err); | ||||
|                 } | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
| @@ -1997,6 +2053,135 @@ describe('HTTP Request Node', function() { | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         it('should use UI proxy for statically configured URL', function (done) { | ||||
|             const url = getTestURL('/postInspect') | ||||
|             const proxyUrl = "http://localhost:" + testProxyPort | ||||
|             const flow = [ | ||||
|                 { id: "n1", type: "http request", wires: [["n2"]], method: "POST", ret: "obj", url: url, proxy: "n3" }, | ||||
|                 { id: "n2", type: "helper" }, | ||||
|                 { id: "n3", type: "http proxy", url: proxyUrl, noproxy: ["foo"] } | ||||
|             ]; | ||||
|             const proxySpy = sinon.spy(httpProxyHelper, 'getProxyForUrl') | ||||
|             const testNode = [httpRequestNode, httpProxyNode]; | ||||
|             deleteProxySetting(); | ||||
|  | ||||
|             // static URL set in the nodes configuration will cause the proxy setup to be called | ||||
|             // no no need to send a message to the node | ||||
|             helper.load(testNode, flow, function () { | ||||
|                 try { | ||||
|                     // ensure getProxyForUrl was called and returned the correct proxy URL | ||||
|                     proxySpy.calledOnce.should.be.true() | ||||
|                     proxySpy.calledWith(url, { env: { no_proxy: "foo", http_proxy: proxyUrl, https_proxy: proxyUrl } }).should.be.true() | ||||
|                     proxySpy.returnValues[0].should.be.equal(proxyUrl) | ||||
|                     done(); | ||||
|                 } catch (err) { | ||||
|                     done(err); | ||||
|                 } | ||||
|             }); | ||||
|         }); | ||||
|         it('should use UI proxy for HTTP URL passed in via msg', function (done) { | ||||
|             const url = getTestURL('/postInspect') | ||||
|             const proxyUrl = "http://localhost:" + testProxyPort | ||||
|             const flow = [ | ||||
|                 { id: "n1", type: "http request", wires: [["n2"]], method: "POST", ret: "obj", url: "", proxy: "n3" }, | ||||
|                 { id: "n2", type: "helper" }, | ||||
|                 { id: "n3", type: "http proxy", url: proxyUrl, noproxy: ["foo,bar"] } | ||||
|             ]; | ||||
|             const proxySpy = sinon.spy(httpProxyHelper, 'getProxyForUrl') | ||||
|             const testNode = [httpRequestNode, httpProxyNode]; | ||||
|             deleteProxySetting(); | ||||
|             helper.load(testNode, flow, function () { | ||||
|                 const n1 = helper.getNode("n1"); | ||||
|                 const n2 = helper.getNode("n2"); | ||||
|                 try { | ||||
|                     proxySpy.calledOnce.should.be.false() // proxy setup should not be called when there is no URL to check needs proxying | ||||
|                 } catch (err) { | ||||
|                     done(err); | ||||
|                     return | ||||
|                 } | ||||
|                 n2.on("input", function (msg) { | ||||
|                     try { | ||||
|                         // ensure getProxyForUrl was called and returned the correct proxy URL | ||||
|                         proxySpy.calledOnce.should.be.true() | ||||
|                         proxySpy.calledWith(url, { env: { no_proxy: "foo,bar", http_proxy: proxyUrl, https_proxy: proxyUrl } }).should.be.true() | ||||
|                         proxySpy.returnValues[0].should.be.equal(proxyUrl) | ||||
|                         done(); | ||||
|                     } catch (err) { | ||||
|                         done(err); | ||||
|                     } | ||||
|                 }); | ||||
|                 n1.receive({ url: url }); | ||||
|             }); | ||||
|         }); | ||||
|         it('should use UI proxy for HTTPS URL passed in via msg', function (done) { | ||||
|             const url = getSslTestURL('/postInspect') | ||||
|             const proxyUrl = "http://localhost:" + testProxyPort | ||||
|             const flow = [ | ||||
|                 { id: "n1", type: "http request", wires: [["n2"]], method: "POST", ret: "obj", url: "", proxy: "n3" }, | ||||
|                 { id: "n2", type: "helper" }, | ||||
|                 { id: "n3", type: "http proxy", url: proxyUrl, noproxy: ["foo,bar,baz"] } | ||||
|             ]; | ||||
|             const proxySpy = sinon.spy(httpProxyHelper, 'getProxyForUrl') | ||||
|             const testNode = [httpRequestNode, httpProxyNode]; | ||||
|             deleteProxySetting(); | ||||
|             helper.load(testNode, flow, function () { | ||||
|                 const n1 = helper.getNode("n1"); | ||||
|                 const n2 = helper.getNode("n2"); | ||||
|                 try { | ||||
|                     proxySpy.calledOnce.should.be.false() // proxy setup should not be called when there is no URL to check needs proxying | ||||
|                 } catch (err) { | ||||
|                     done(err); | ||||
|                     return | ||||
|                 } | ||||
|                 n2.on("input", function (msg) { | ||||
|                     try { | ||||
|                         // ensure getProxyForUrl was called and returned the correct proxy URL | ||||
|                         proxySpy.calledOnce.should.be.true() | ||||
|                         proxySpy.calledWith(url, { env: { no_proxy: "foo,bar,baz", http_proxy: proxyUrl, https_proxy: proxyUrl } }).should.be.true() | ||||
|                         proxySpy.returnValues[0].should.be.equal(proxyUrl) | ||||
|                         done(); | ||||
|                     } catch (err) { | ||||
|                         done(err); | ||||
|                     } | ||||
|                 }); | ||||
|                 n1.receive({ url: url }); | ||||
|             }); | ||||
|         }); | ||||
|         it('should not use UI proxy if noproxy excludes it', function (done) { | ||||
|             const url = getSslTestURL('/postInspect') | ||||
|             const proxyUrl = "http://localhost:" + testProxyPort | ||||
|             const flow = [ | ||||
|                 { id: "n1", type: "http request", wires: [["n2"]], method: "POST", ret: "obj", url: "", proxy: "n3" }, | ||||
|                 { id: "n2", type: "helper" }, | ||||
|                 { id: "n3", type: "http proxy", url: proxyUrl, noproxy: ["foo,localhost,baz"] } | ||||
|             ]; | ||||
|             const proxySpy = sinon.spy(httpProxyHelper, 'getProxyForUrl') | ||||
|             const testNode = [httpRequestNode, httpProxyNode]; | ||||
|             deleteProxySetting(); | ||||
|             helper.load(testNode, flow, function () { | ||||
|                 const n1 = helper.getNode("n1"); | ||||
|                 const n2 = helper.getNode("n2"); | ||||
|                 try { | ||||
|                     proxySpy.calledOnce.should.be.false() // proxy setup should not be called when there is no URL to check needs proxying | ||||
|                 } catch (err) { | ||||
|                     done(err); | ||||
|                     return | ||||
|                 } | ||||
|                 n2.on("input", function (msg) { | ||||
|                     try { | ||||
|                         // ensure getProxyForUrl was called and returned no proxy | ||||
|                         proxySpy.calledOnce.should.be.true() | ||||
|                         proxySpy.calledWith(url, { env: { no_proxy: "foo,localhost,baz", http_proxy: proxyUrl, https_proxy: proxyUrl } }).should.be.true() | ||||
|                         proxySpy.returnValues[0].should.be.equal('') | ||||
|                         done(); | ||||
|                     } catch (err) { | ||||
|                         done(err); | ||||
|                     } | ||||
|                 }); | ||||
|                 n1.receive({ url: url }); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|     }); | ||||
|     describe('authentication', function() { | ||||
|  | ||||
|   | ||||
| @@ -98,7 +98,7 @@ describe('BATCH node', function() { | ||||
|                 var n2 = helper.getNode("n2"); | ||||
|                 check_data(n1, n2, results, done); | ||||
|                 for(var i = 0; i < 6; i++) { | ||||
|                     n1.receive({payload: i}); | ||||
|                     n1.receive({payload: i, parts: { count:6, index:i }}); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
| @@ -168,6 +168,25 @@ describe('BATCH node', function() { | ||||
|             check_count(flow, results, done); | ||||
|         }); | ||||
|  | ||||
|         it('should create seq. with count (more sent than count)', function(done) { | ||||
|             var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 4, overlap: 0, interval: 10, allowEmptySequence: false, topics: [], wires:[["n2"]]}, | ||||
|                         {id:"n2", type:"helper"}]; | ||||
|             var results = [ | ||||
|                 [0, 1, 2, 3] | ||||
|             ]; | ||||
|             check_count(flow, results, done); | ||||
|         }); | ||||
|  | ||||
|         it('should create seq. with count and terminate early if parts honoured', function(done) { | ||||
|             var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 4, overlap: 0, interval: 10, allowEmptySequence:false, honourParts:true, topics: [], wires:[["n2"]]}, | ||||
|                         {id:"n2", type:"helper"}]; | ||||
|             var results = [ | ||||
|                 [0, 1, 2, 3], | ||||
|                 [4, 5] | ||||
|             ]; | ||||
|             check_count(flow, results, done); | ||||
|         }); | ||||
|  | ||||
|         it('should create seq. with count and overlap', function(done) { | ||||
|             var flow = [{id:"n1", type:"batch", name: "BatchNode", mode: "count", count: 3, overlap: 2, interval: 10, allowEmptySequence: false, topics: [], wires:[["n2"]]}, | ||||
|                         {id:"n2", type:"helper"}]; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user