Compare commits

..

74 Commits

Author SHA1 Message Date
Nick O'Leary
54e6d60fe5 Add simple caching of env var lookup 2024-01-05 21:07:20 +00:00
Nick O'Leary
c2710f4f6f Add auto-complete for env vars 2023-12-20 17:52:52 +00:00
Nick O'Leary
20187b51b1 Fix up cache scope 2023-12-20 16:51:34 +00:00
Nick O'Leary
4be6d57d98 Apply suggestions from code review
Co-authored-by: Gauthier Dandele <92022724+GogoVega@users.noreply.github.com>
2023-12-20 16:39:52 +00:00
Nick O'Leary
a77f8cc3e9 Clear context cache when closing edit dialog 2023-12-15 15:09:15 +00:00
Nick O'Leary
ea4c0cdbee Fix error when switching context types 2023-12-14 17:14:56 +00:00
Nick O'Leary
7197153fd5 Support bracket-notation in auto complete when needed 2023-12-11 21:18:44 +00:00
Nick O'Leary
b9c1dedab3 Add auto-complete to flow/global typedInput types 2023-12-11 17:55:02 +00:00
Nick O'Leary
918943816f Merge branch 'master' into dev 2023-12-08 10:27:04 +00:00
Nick O'Leary
0e8d312794 Merge pull request #4476 from node-red/rel313
Bump for 3.1.3 release
2023-12-07 20:30:08 +00:00
Nick O'Leary
c584d51432 Bump for 3.1.3 release 2023-12-07 18:27:33 +00:00
Nick O'Leary
2366b4508f Merge pull request #4475 from node-red/fix-nls
Add missing en-us messages
2023-12-07 18:25:37 +00:00
Nick O'Leary
2f1565fbc9 Add missing en-us messages 2023-12-07 18:19:37 +00:00
Nick O'Leary
7fd0ecf721 Merge pull request #4474 from node-red/rel312
Bump for 3.1.2 release
2023-12-07 14:23:56 +00:00
Nick O'Leary
37d1539fda Bump for 3.1.2 release 2023-12-07 14:12:53 +00:00
Nick O'Leary
1e518396d6 Merge pull request #4470 from wangyiyi2056/Add-action-list-Chinese-translation
Added action list Chinese (Simplified and Traditional) translation + v3.1.1 changes
2023-12-07 13:59:54 +00:00
Nick O'Leary
617b98ed49 Merge pull request #4466 from GogoVega/add-action-list-translation
Add French translation of `action-list` + v3.1.1 changes
2023-12-07 13:56:25 +00:00
Nick O'Leary
6d2a870812 Merge pull request #4472 from node-red/fix-nested-groups-in-subflow
Ensure nested groups inside subflows have their g props remapped
2023-12-07 13:42:14 +00:00
Nick O'Leary
2963f3f1b8 Ensure nested groups inside subflows have their g props remapped 2023-12-07 11:47:02 +00:00
wangyiyi2056
17e4bdbff1 Merge branch 'master' into Add-action-list-Chinese-translation 2023-12-07 09:19:46 +08:00
Nick O'Leary
f3dd5770d9 Merge pull request #4471 from node-red/relax-validation
Relax some node validators to allow undefined value
2023-12-06 12:11:30 +00:00
Nick O'Leary
eebab4a921 Relax some node validators to allow undefined value 2023-12-06 10:30:49 +00:00
Nick O'Leary
b06494c5be Merge pull request #4465 from node-red/4464-fix-switch-typeof-validation
Fix switch validation of typeof field
2023-12-06 10:09:39 +00:00
wangyiyi2056
a0562bef81 Added action list Chinese translation + v3.1.1 2023-12-05 22:50:03 +08:00
Nick O'Leary
33cf34f7c7 Merge branch 'master' into dev 2023-12-04 15:58:45 +00:00
Nick O'Leary
eff063a748 Merge pull request #4467 from node-red/fix-group-mouse-pointer
Use move cursor when hovering on group border
2023-12-04 15:55:15 +00:00
Nick O'Leary
94abaaff1e Use move cursor when hovering on group border 2023-12-04 11:49:29 +00:00
GogoVega
03732869e4 Fix that little guy hiding 2023-12-02 18:53:06 +01:00
GogoVega
41868e2652 Apply v3.1.1 changes 2023-12-02 16:16:03 +01:00
GogoVega
81bfba3cea Add actions list translation 2023-12-02 16:10:52 +01:00
Nick O'Leary
8a04eb2e29 Update packages/node_modules/@node-red/nodes/core/function/10-switch.html 2023-12-01 19:40:49 +00:00
Nick O'Leary
1777fc749d Fix switch validation of typeof field
Fixes #4464
2023-12-01 13:11:07 +00:00
Nick O'Leary
fd32ee09ff Merge pull request #4462 from node-red/rel311
Bump for 3.1.1 release
2023-11-30 16:14:40 +00:00
Nick O'Leary
4bb2157cab Bump for 3.1.1 release 2023-11-30 15:49:57 +00:00
Nick O'Leary
47f20cc86a Merge pull request #4404 from node-red/dependabot/github_actions/github-actions-62712cefbd
Bump the github-actions group with 2 updates
2023-11-30 15:15:42 +00:00
Nick O'Leary
5fc4526c70 Merge pull request #4461 from node-red/fix-debug-filter
Fix debug filter
2023-11-30 15:15:04 +00:00
Nick O'Leary
9fe653f821 Merge branch 'master' into fix-debug-filter 2023-11-30 15:08:14 +00:00
Nick O'Leary
55da21ed15 Merge pull request #4460 from node-red/4369-improve-error-handling-in-subflowmodule-parsing
Handle unknown node reference inside subflow module
2023-11-30 15:07:58 +00:00
Nick O'Leary
7ebf84f38c Fix debug filter 2023-11-30 14:50:28 +00:00
Nick O'Leary
d42e75ebd0 Handle unknown node reference inside subflow module 2023-11-30 14:46:43 +00:00
Nick O'Leary
28825049fe Merge pull request #4459 from node-red/4401-fix-debug-window
Fix various issues with debug pop-out window
2023-11-30 11:36:16 +00:00
Nick O'Leary
1c3644e338 Fix various issues with debug pop-out window 2023-11-30 11:23:35 +00:00
Nick O'Leary
2dfabb523b Merge pull request #4457 from node-red/4456-fix-group-in-subflow-lookup
Ensure subflow instances keep track of their groups
2023-11-29 16:41:52 +00:00
Nick O'Leary
3e2d20e536 Merge pull request #4455 from GogoVega/4429-fix-validateNodeProperty
Fix `validateNodeProperty` without validator provided
2023-11-29 16:19:41 +00:00
Nick O'Leary
ee7ee083b0 Merge pull request #4440 from node-red/4429-add-typed-validators
Add validators to any fields using msg-typed Input
2023-11-29 16:18:53 +00:00
Nick O'Leary
21f807aa66 Merge pull request #4453 from node-red/4413-debounce-uninstall-notifications
Debounce node-removed notifications
2023-11-29 16:11:18 +00:00
Nick O'Leary
6b088bda12 Merge pull request #4452 from node-red/4443-add-modules-install-audit-event
Add modules.install audit event when external module installed
2023-11-29 16:11:04 +00:00
Nick O'Leary
a32ee869ae Merge pull request #4448 from bonanitech/first-commit-has-no-parents
Don't try to load the parents of the first commit
2023-11-29 16:10:42 +00:00
GogoVega
a2d7772958 Fix validateNodeProperty if no validator provided 2023-11-28 20:13:25 +01:00
Stephen McLaughlin
6ec052be18 Merge pull request #4454 from node-red/4380-mqtt-undefined-value
Guard against node.broker being undefined
2023-11-27 17:48:02 +00:00
Nick O'Leary
9c71d52d69 Check node.broker is a string before trying to use it
Fixes #4380
2023-11-27 17:27:32 +00:00
Nick O'Leary
171c146ec5 Debounce node-removed notifications
Fixes #4413
2023-11-27 17:14:15 +00:00
Nick O'Leary
bc6afa2164 Add modules.install audit event when external module installed 2023-11-27 16:58:14 +00:00
Stephen McLaughlin
3c036257ef Merge pull request #4451 from node-red/4446-allow-import-of-subpath-modules
Allow import of modules with subpath in specifier
2023-11-27 16:57:32 +00:00
Nick O'Leary
6633730bf1 Allow import of modules with subpath in specifier
Fixes #4446
2023-11-27 16:44:56 +00:00
Mauricio Bonani
2964a4da5e Don't try to load the parents of the first commit 2023-11-24 16:22:29 -05:00
Nick O'Leary
722fe02933 Add validators to any fields using msg-typed Input
Fixes #4429
2023-11-20 17:17:52 +00:00
Nick O'Leary
5b5b06cc06 Merge pull request #4406 from node-red/tcp-request-node-reset-when-in-stay-connected-mode
Let msg.reset  reset Tcp request node connection when in stay connected mode
2023-11-07 17:42:17 +00:00
Dave Conway-Jones
f49f692ffa Better fix for TCP node reset
now handles reply out node,
and can specify which connection to reset.
2023-11-03 11:57:16 +00:00
Nick O'Leary
08c6ea94cb Merge pull request #4347 from ZJvandeWeg/zj-remove-production-flag-npm
npm: Remove production flag on npm invocation
2023-11-01 14:18:14 +01:00
Nick O'Leary
fea1da5542 Merge pull request #4402 from node-red/Let-debug-status-length-be-settable
Let debug node status msg length be settable via settings
2023-11-01 14:13:38 +01:00
Dave Conway-Jones
32e8f4eac6 Add help info 2023-11-01 12:33:57 +00:00
Dave Conway-Jones
bfe5a8a986 Update 31-tcpin.js
don't send if payload not defined.
2023-11-01 12:27:11 +00:00
Dave Conway-Jones
f2cb5ea44e Allow msg.reset to reset connection when tcp request in stay connected mode 2023-11-01 12:07:50 +00:00
dependabot[bot]
60593fed4a Bump the github-actions group with 2 updates
Bumps the github-actions group with 2 updates: [actions/setup-node](https://github.com/actions/setup-node) and [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request).


Updates `actions/setup-node` from 3 to 4
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v3...v4)

Updates `peter-evans/create-pull-request` from 2 to 5
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/v2...v5)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-01 07:42:43 +00:00
Dave Conway-Jones
c7335ed25b Let debug node status msg length be settable via settings 2023-10-31 09:11:17 +00:00
Nick O'Leary
eb940d6d57 Merge pull request #4367 from hlovdal/timer_testing_fix
Timer testing fix
2023-09-25 18:19:30 +01:00
Håkon Løvdal
9091935d77 Update variable names 2023-09-25 18:53:11 +02:00
Håkon Løvdal
34e8d2b051 Add workaround for timers triggering too early in test 2023-09-24 18:16:59 +02:00
Håkon Løvdal
0c2ab13c48 Print all delta values in case of error, not just the last value
Which might not even be the one triggering the error condition.
2023-09-24 18:16:59 +02:00
Håkon Løvdal
9489953a8f Introduce timeout constant 2023-09-24 18:16:59 +02:00
ZJ van de Weg
54d4079457 npm: Remove production flag on npm invocation
When installing packages the `--production` flag used to be added to the
arguments that `npm` received. As npm wants developers to use the
`--omit=dev` flag instead it warned users on STDERR. Standard error was
captured by Node-RED and output to the logs as being an error. This
caught users off-guard and they expected something to have gone
wrong.

With this change the `--omit=dev` is used instead, to remove the
warning.

This change works for NPM of version 8 and beyond[1], included in
Node.JS 16. This change will not work on NPM version 6[2] which is included
in Node.JS 14[3].

[1]: https://docs.npmjs.com/cli/v8/commands/npm-install#omit
[2]: https://docs.npmjs.com/cli/v6/commands/npm-install
[3]: https://nodejs.org/en/download/releases#looking-for-latest-release-of-a-version-branch
2023-09-17 08:43:11 +02:00
Nick O'Leary
cef3a01042 Merge pull request #4322 from node-red/prep4
Bump to 4.0.0-dev
2023-09-06 15:04:41 +01:00
Nick O'Leary
0c042abcab Bump to 4.0.0-dev 2023-09-06 14:45:45 +01:00
48 changed files with 1135 additions and 334 deletions

View File

@@ -27,12 +27,12 @@ jobs:
with:
repository: 'node-red/node-red.github.io'
path: 'node-red.github.io'
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: '16'
- run: node ./node-red/.github/scripts/update-node-red-docker.js
- name: Create Docker Pull Request
uses: peter-evans/create-pull-request@v2
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.NR_REPO_TOKEN }}
committer: GitHub <noreply@github.com>
@@ -48,7 +48,7 @@ jobs:
This PR was auto-generated by a GitHub Action. Any questions, speak to @knolleary
- run: node ./node-red/.github/scripts/update-node-red-website.js
- name: Create Website Pull Request
uses: peter-evans/create-pull-request@v2
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.NR_REPO_TOKEN }}
committer: GitHub <noreply@github.com>

View File

@@ -21,7 +21,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install Dependencies

View File

@@ -1,3 +1,77 @@
#### 3.1.3: Maintenance Release
Editor
- Add missing en-us messages (#4475) @knolleary
#### 3.1.2: Maintenance Release
Editor
- Relax some node validators to allow undefined value (#4471) @knolleary
- Fix switch validation of typeof field (#4465) @knolleary
- Use move cursor when hovering on group border (#4467) @knolleary
- Added action list Chinese (Simplified and Traditional) translation + v3.1.1 changes (#4470) @wangyiyi2056
- Add French translation of `action-list` + v3.1.1 changes (#4466) @GogoVega
Runtime
- Ensure nested groups inside subflows have their g props remapped (#4472) @knolleary
#### 3.1.1: Maintenance Release
Editor
- Fix debug filter (#4461) @knolleary
- Fix various issues with debug pop-out window (#4459) @knolleary
- Ensure subflow instances keep track of their groups (#4457) @knolleary
- Fix `validateNodeProperty` without validator provided (#4455) @GogoVega
- Debounce node-removed notifications (#4453) @knolleary
- Don't try to load the parents of the first commit (#4448) @bonanitech
- Allow a theme to specifiy which theme mermaid should use (#4441) @knolleary
- Update browser title with flow name if set (#4427) @knolleary
- Ensure typeSearch handles undefined node definitions (#4423) @knolleary
- Ensure group w/h are imported if present (#4426) @knolleary
- Hide node status background when there is no status to show (#4425) @knolleary
- Add a close button to the restart-required notification (#4407) @knolleary
- Extend typedInput "num" type validity check to NaN, binary, octal & hex (#4371) @ralphwetzel
- Fix unintended new line in node name (#4399) @kazuhitoyokoi
- Ctrl-Enter does not close tray (Monaco) #4377 (#4382) @hazymat
- fix buffer viewer to handle 0b style binary (#4393) @dceejay
- Rework mermaid integration to support off-DOM rendering (#4364) @knolleary
- Add missing nls labels to context menu (#4365) @knolleary
Runtime
- Bump the github-actions group with 2 updates (#4404) @app/dependabot
- Handle unknown node reference inside subflow module (#4460) @knolleary
- Add modules.install audit event when external module installed (#4452) @knolleary
- Allow import of modules with subpath in specifier (#4451) @knolleary
- Update node-red-admin version (#4438) @knolleary
- Handle false-like env vars properly (#4411) @knolleary
- Only save settings once during node load process (#4409) @knolleary
- Ensure global-config nodes lookup cred values properly (#4405) @knolleary
- Handle credential env var evaluation when no value set (#4362) @knolleary
- Don't commit package-lock.json (#4354) @bonanitech
- Fix env evaluation when one env references another in the same object (#4361) @knolleary
- Add dependabot for Github Actions (#4312) @Rotzbua
- Update outdated Github Actions (#4311) @Rotzbua
- github: Request `npm run test` in PR template (#4348) @ZJvandeWeg
- Add French translation of v3.1.0-beta.4 changes + slight improvements (#4329) @GogoVega
- Handle nodes with multiple input handlers properly (#4332) @knolleary
- Soften the language around unrequited PRs (#4351) @knolleary
Nodes
- CSV: make CSV export way faster by not re-allocating and handling huge string (#4349) @Fadoli
- Delay: Fix regression in delay node to not pass on msg.reset (#4350) @dceejay
- Link Call: Handle undefined linkType value for existing link-call nodes (#4331) @knolleary
- MQTT: Guard against node.broker being undefined (#4454) @knolleary
- MQTT: check topic length > 0 before publish (#4416) @dceejay
- Switch/Change: Improve validation of switch/change node rules (#4368) @knolleary
- Template: Fix height of description editor in template node (#4346) @kazuhitoyokoi
- Various: Add validators to any fields using msg-typed Input (#4440) @knolleary
#### 3.1.0: Milestone Release
Editor

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "3.1.0",
"version": "4.0.0-dev",
"description": "Low-code programming for event-driven applications",
"homepage": "https://nodered.org",
"license": "Apache-2.0",

View File

@@ -33,6 +33,9 @@ module.exports = {
store: req.query['store'],
req: apiUtils.getRequestLogObject(req)
}
if (req.query['keysOnly'] !== undefined) {
opts.keysOnly = true
}
runtimeAPI.context.getValue(opts).then(function(result) {
res.json(result);
}).catch(function(err) {

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-api",
"version": "3.1.0",
"version": "4.0.0-dev",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,8 +16,8 @@
}
],
"dependencies": {
"@node-red/util": "3.1.0",
"@node-red/editor-client": "3.1.0",
"@node-red/util": "4.0.0-dev",
"@node-red/editor-client": "4.0.0-dev",
"bcryptjs": "2.4.3",
"body-parser": "1.20.2",
"clone": "2.1.2",

View File

@@ -113,7 +113,7 @@
"displayStatus": "Show node status",
"displayConfig": "Configuration nodes",
"import": "Import",
"importExample": "Import Example Flow",
"importExample": "Import example flow",
"export": "Export",
"search": "Search flows",
"searchInput": "search your flows",
@@ -130,6 +130,11 @@
"editPalette": "Manage palette",
"other": "Other",
"showTips": "Show tips",
"showNodeHelp": "Show node help",
"enableSelectedNodes": "Enable selected nodes",
"disableSelectedNodes": "Disable selected nodes",
"showSelectedNodeLabels": "Show selected node labels",
"hideSelectedNodeLabels": "Hide selected node labels",
"showWelcomeTours": "Show guided tours for new versions",
"help": "Node-RED website",
"projects": "Projects",
@@ -511,8 +516,8 @@
"selectAllConnected": "Select connected",
"addRemoveNode": "Add/remove node from selection",
"editSelected": "Edit selected node",
"deleteSelected": "Delete selected nodes or link",
"deleteReconnect": "Delete and Reconnect",
"deleteSelected": "Delete selection",
"deleteReconnect": "Delete and reconnect",
"importNode": "Import nodes",
"exportNode": "Export nodes",
"nudgeNode": "Move selected nodes (1px)",
@@ -1227,6 +1232,7 @@
}
},
"contextMenu": {
"showActionList": "Show action list",
"insert": "Insert",
"node": "Node",
"junction": "Junction",

View File

@@ -1215,7 +1215,8 @@
"validator": {
"errors": {
"invalid-json": "Données JSON invalides : __error__",
"invalid-prop": "Expression de propriété non valide",
"invalid-expr": "Expression JSONata invalide : __error__",
"invalid-prop": "Expression de propriété invalide",
"invalid-num": "Numéro invalide",
"invalid-regexp": "Modèle d'entrée non valide",
"invalid-regex-prop": "__prop__: modèle d'entrée non valide",
@@ -1235,5 +1236,159 @@
"environment": "Environment",
"header": "Variables d'environnement globales",
"revert": "Rétablir"
},
"action-list": {
"toggle-show-tips": "Basculer l'affichage des astuces",
"show-about": "Afficher la description de Node-RED",
"show-welcome-tour": "Afficher la visite de bienvenue",
"show-next-tab": "Afficher l'onglet suivant",
"show-previous-tab": "Afficher l'onglet précédent",
"add-flow": "Ajouter un flux",
"add-flow-to-right": "Ajouter un flux à droite",
"edit-flow": "Modifier le flux",
"remove-flow": "Supprimer le flux",
"enable-flow": "Activer le flux",
"disable-flow": "Désactiver le flux",
"hide-flow": "Masquer le flux",
"hide-other-flows": "Masquer les autres flux",
"hide-all-flows": "Masquer tous les flux",
"show-all-flows": "Afficher tous les flux",
"show-last-hidden-flow": "Afficher le dernier flux masqué",
"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",
"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",
"cut-selection-to-internal-clipboard": "Couper la sélection dans le presse-papiers",
"paste-from-internal-clipboard": "Coller depuis le presse-papiers",
"detach-selected-nodes": "Détacher les noeuds sélectionnés",
"delete-selection": "Supprimer la sélection",
"delete-selection-and-reconnect": "Supprimer la sélection et reconnecter",
"edit-selected-node": "Modifier le noeud sélectionné",
"go-to-selection": "Aller à la sélection",
"undo": "Annuler les modifications",
"redo": "Rétablir les modifications",
"select-all-nodes": "Sélectionner tous les noeuds",
"select-none": "Sélectionner un noeud",
"enable-selected-nodes": "Activer les noeuds sélectionnés",
"disable-selected-nodes": "Désactiver les noeuds sélectionnés",
"toggle-show-grid": "Basculer l'affichage de la grille",
"toggle-snap-grid": "Basculer l'aide au placement des noeuds",
"toggle-status": "Commuter l'état",
"show-selected-node-labels": "Afficher les étiquettes des noeuds sélectionnés",
"hide-selected-node-labels": "Masquer les étiquettes des noeuds sélectionnés",
"scroll-view-up": "Faire défiler vers le haut",
"scroll-view-right": "Faire défiler vers la droite",
"scroll-view-down": "Faire défiler vers le bas",
"scroll-view-left": "Faire défiler vers la gauche",
"step-view-up": "Faire défiler d'une unité vers le haut",
"step-view-right": "Faire défiler d'une unité vers la droite",
"step-view-down": "Faire défiler d'une unité vers le bas",
"step-view-left": "Faire défiler d'une unité vers la gauche",
"move-selection-up": "Déplacer la sélection vers le haut",
"move-selection-right": "Déplacer la sélection vers la droite",
"move-selection-down": "Déplacer la sélection vers le bas",
"move-selection-left": "Déplacer la sélection vers la gauche",
"move-selection-forwards": "Avancer la sélection",
"move-selection-backwards": "Reculer la sélection",
"move-selection-to-front": "Déplacer la sélection vers l'avant",
"move-selection-to-back": "Déplacer la sélection vers l'arrière",
"step-selection-up": "Déplacer la sélection d'une unité vers le haut",
"step-selection-right": "Déplacer la sélection d'une unité vers la droite",
"step-selection-down": "Déplacer la sélection d'une unité vers le bas",
"step-selection-left": "Déplacer la sélection d'une unité vers la gauche",
"select-connected-nodes": "Sélectionner les noeuds connectés",
"select-downstream-nodes": "Sélectionner les noeuds connectés en aval",
"select-upstream-nodes": "Sélectionner les noeuds connectés en amont",
"go-to-next-node": "Aller au noeud suivant",
"go-to-previous-node": "Aller au noeud précédent",
"go-to-next-sibling": "Aller au noeud frère suivant",
"go-to-previous-sibling": "Aller au noeud frère précédent",
"go-to-nearest-node-on-left": "Aller au noeud gauche le plus proche",
"go-to-nearest-node-on-right": "Aller au noeud droit le plus proche",
"go-to-nearest-node-above": "Aller au noeud supérieur le plus proche",
"go-to-nearest-node-below": "Aller au noeud le plus proche ci-dessous",
"align-selection-to-grid": "Aligner la sélection",
"align-selection-to-left": "Aligner la sélection à gauche",
"align-selection-to-right": "Aligner la sélection à droite",
"align-selection-to-top": "Aligner la sélection en haut",
"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",
"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",
"split-wire-with-link-nodes": "Diviser le fil avec des noeuds de liaison",
"generate-node-names": "Générer les noms de noeuds",
"show-user-settings": "Afficher les paramètres utilisateur",
"show-help": "Afficher l'aide",
"toggle-palette": "Basculer l'affichage de la palette",
"show-event-log": "Afficher le journal des événements",
"manage-palette": "Gérer la palette",
"toggle-sidebar": "Basculer l'affichage de la barre latérale",
"show-info-tab": "Afficher l'onglet d'informations sur le noeud",
"show-help-tab": "Afficher l'onglet d'aide du noeud",
"show-config-tab": "Afficher l'onglet du noeud de configuration",
"select-all-config-nodes": "Sélectionner tous les noeuds de configuration",
"delete-config-selection": "Supprimer le noeud de configuration sélectionné",
"show-context-tab": "Afficher l'onglet des données contextuelles",
"create-subflow": "Créer un sous-flux",
"convert-to-subflow": "Convertir la sélection en sous-flux",
"group-selection": "Grouper la sélection",
"ungroup-selection": "Dissocier la sélection",
"merge-selection-to-group": "Fusionner la sélection dans le groupe",
"remove-selection-from-group": "Supprimer la sélection du groupe",
"copy-group-style": "Copier le style du groupe",
"paste-group-style": "Coller le style du groupe",
"show-export-dialog": "Afficher la boîte de dialogue d'exportation",
"show-import-dialog": "Afficher la boîte de dialogue d'importation",
"show-library-export-dialog": "Afficher la boîte de dialogue d'exportation de la bibliothèque",
"show-library-import-dialog": "Afficher la boîte de dialogue d'importation de bibliothèque",
"show-examples-import-dialog": "Afficher la boîte de dialogue d'importation d'exemples",
"search": "Rechercher",
"search-previous": "Recherche précédente",
"search-next": "Recherche suivante",
"show-action-list": "Afficher la liste d'actions",
"confirm-edit-tray": "Confirmer la modification",
"cancel-edit-tray": "Annuler la modification",
"show-remote-diff": "Afficher les différences avec les modifications distantes",
"deploy-flows": "Déployer des flux",
"restart-flows": "Redémarrer les flux",
"set-deploy-type-to-full": "Définir le déploiement sur 'tout'",
"set-deploy-type-to-modified-flows": "Définir le déploiement sur 'flux modifiés'",
"set-deploy-type-to-modified-nodes": "Définir le déploiement sur 'noeuds modifiés'",
"show-debug-tab": "Afficher l'onglet de débogage",
"clear-debug-messages": "Supprimer les messages de débogage",
"clear-filtered-debug-messages": "Supprimer les messages de débogage filtrés",
"activate-selected-debug-nodes": "Activer les noeuds de débogage sélectionnés",
"activate-all-debug-nodes": "Activer tous les noeuds de débogage",
"activate-all-flow-debug-nodes": "Activer tous les noeuds de débogage dans un flux",
"deactivate-selected-debug-nodes": "Désactiver les noeuds de débogage sélectionnés",
"deactivate-all-debug-nodes": "Désactiver tous les noeuds de débogage",
"deactivate-all-flow-debug-nodes": "Désactiver tous les noeuds de débogage dans un flux",
"zoom-in": "Zoomer",
"zoom-out": "Dézoomer",
"zoom-reset": "Réinitialiser le zoom",
"toggle-navigator": "Basculer l'affichage du navigateur",
"show-system-info": "Afficher les informations système",
"split-wires-with-junctions": "Diviser les fils avec des jonctions",
"new-project": "Nouveau projet",
"open-project": "Ouvrir le projet",
"show-project-settings": "Afficher les paramètres du projet",
"show-version-control-tab": "Afficher l'onglet de contrôle de version",
"start-flows": "Démarrer les flux",
"stop-flows": "Arrêter les flux",
"copy-item-url": "Copier l'URL de l'élément",
"copy-item-edit-url": "Copier l'URL de modification de l'élément",
"move-flow-to-start": "Déplacer le flux jusqu'au début",
"move-flow-to-end": "Déplacer le flux jusqu'à la fin",
"show-global-env": "Afficher les variables d'environnement globales",
"lock-flow": "Verrouiller le flux",
"unlock-flow": "Déverrouiller le flux",
"show-node-help": "Afficher l'aide du noeud"
}
}

View File

@@ -23,7 +23,11 @@
"position": "位置",
"enable": "启用",
"disable": "禁用",
"upload": "上传"
"upload": "上传",
"lock": "锁定",
"unlock": "解锁",
"locked": "锁定",
"unlocked": "解锁"
},
"type": {
"string": "字符串",
@@ -68,7 +72,13 @@
"enabled": "有效",
"disabled": "无效",
"info": "详细描述",
"selectNodes": "点击节点来选择"
"selectNodes": "点击节点来选择",
"enableFlow": "启用流程",
"disableFlow": "禁用流程",
"lockFlow": "锁定流程",
"unlockFlow": "解除锁定",
"moveToStart": "移动到起始",
"moveToEnd": "移动到末尾"
},
"menu": {
"label": {
@@ -101,6 +111,7 @@
"displayStatus": "显示节点状态",
"displayConfig": "修改节点配置",
"import": "导入",
"importExample": "导入示例流程",
"export": "导出",
"search": "查找流程",
"searchInput": "查找流程",
@@ -142,7 +153,12 @@
"moveToBack": "置于底层",
"moveToFront": "置于顶层",
"moveBackwards": "向后移动",
"moveForwards": "向前移动"
"moveForwards": "向前移动",
"showNodeHelp":"显示节点帮助",
"enableSelectedNodes":"启用当前选中节点",
"disableSelectedNodes":"禁用当前选中节点",
"showSelectedNodeLabels":"显示选中的节点标签",
"hideSelectedNodeLabels":"隐藏选中的节点标签"
}
},
"actions": {
@@ -403,6 +419,7 @@
},
"errors": {
"noNodesSelected": "<strong>无法创建子流程</strong>: 未选择节点",
"acrossMultipleGroups": "无法跨多个组创建子流",
"multipleInputsToSelection": "<strong>无法创建子流程</strong>: 多个输入到了选择"
}
},
@@ -491,12 +508,14 @@
"unassigned": "未分配",
"global": "全局",
"workspace": "工作区",
"editor": "编辑对话框",
"selectAll": "选择所有节点",
"selectNone": "取消所有选择",
"selectAllConnected": "选择所有连接的节点",
"addRemoveNode": "从选择中添加/删除节点",
"editSelected": "编辑选定节点",
"deleteSelected": "删除选定节点或链接",
"deleteReconnect": "删除并重新连接",
"importNode": "导入节点",
"exportNode": "导出节点",
"nudgeNode": "移动所选节点(1px)",
@@ -571,6 +590,7 @@
"editor": {
"title": "面板管理",
"palette": "控制板",
"allCatalogs": "所有目录",
"times": {
"seconds": "秒前",
"minutes": "分前",
@@ -610,6 +630,7 @@
"tab-nodes": "节点",
"tab-install": "安装",
"sort": "排序:",
"sortRelevance": "关联",
"sortAZ": "a-z顺序",
"sortRecent": "日期顺序",
"more": "增加 __count__ 个",
@@ -683,7 +704,11 @@
"empty": "空的",
"globalConfig": "全局配置节点",
"triggerAction": "触发动作",
"find": "在工作区中查找"
"find": "在工作区中查找",
"copyItemUrl": "复制地址",
"copyURL2Clipboard": "复制地址到剪贴板",
"showFlow": "显示流程",
"hideFlow": "隐藏流程"
},
"help": {
"name": "帮助",
@@ -984,7 +1009,10 @@
"quote": "引用",
"link": "链接",
"horizontal-rule": "水平线",
"toggle-preview": "切换预览"
"toggle-preview": "切换预览",
"mermaid": {
"summary": "美人鱼图"
}
},
"bufferEditor": {
"title": "Buffer 编辑器",
@@ -1147,17 +1175,6 @@
"create": "创建分支",
"current": "当前的"
},
"languages": {
"de": "德语",
"en-US": "英文",
"fr": "法语",
"ja": "日语",
"ko": "韩文",
"pt-BR":"葡萄牙语",
"ru":"俄語",
"zh-CN": "简体中文",
"zh-TW": "繁体中文"
},
"create-default-file-set": {
"no-active": "没有活动项目就无法创建默认文件集",
"no-empty": "无法在非空项目上创建默认文件集",
@@ -1188,17 +1205,20 @@
"title": "系统信息"
},
"languages": {
"de": "德语-Deutsch",
"en-US": "英文-English",
"ja": "日语-日本",
"ko": "韩文-한국인",
"ru": "俄语-Русский",
"de": "德语",
"en-US": "英文",
"fr": "法语",
"ja": "日语",
"ko": "韩文",
"pt-BR":"葡萄牙语",
"ru":"俄語",
"zh-CN": "简体中文",
"zh-TW": "繁中文"
"zh-TW": "繁中文"
},
"validator": {
"errors": {
"invalid-json": "无效的 JSON 数据: __error__",
"invalid-expr": "无效的 JSONata 表达式: __error__",
"invalid-prop": "无效的属性表达式",
"invalid-num": "无效的数字",
"invalid-regexp": "输入格式无效",
@@ -1210,9 +1230,15 @@
}
},
"contextMenu": {
"showActionList":"显示动作列表",
"insert": "插入",
"node": "节点",
"junction": "连接点",
"linkNodes": "链接节点"
},
"env-var": {
"environment": "环境配置",
"header": "全局环境变量",
"revert": "重置"
}
}

View File

@@ -270,5 +270,9 @@
"$moment": {
"args": "[str]",
"desc": "使用Moment库获取日期对象。"
},
"$clone": {
"args": "value",
"desc": "安全克隆对象."
}
}

View File

@@ -23,7 +23,11 @@
"position": "位置",
"enable": "啟用",
"disable": "禁用",
"upload": "上傳"
"upload": "上傳",
"lock": "鎖定",
"unlock": "解鎖",
"locked": "鎖定",
"unlocked": "解鎖"
},
"type": {
"string": "字符串",
@@ -38,11 +42,14 @@
}
},
"event": {
"loadPlugins": "加載插件",
"loadPalette": "加載控制板",
"loadNodeCatalogs": "加載節點目錄",
"loadNodes": "加載 __count__ 個節點",
"loadFlows": "加載流程",
"importFlows": "往工作區中加載流程"
"importFlows": "往工作區中加載流程",
"importError": "<p>加載流程錯誤</p><p>__message__</p>",
"loadingProject": "加載項目"
},
"workspace": {
"defaultName": "流程__number__",
@@ -51,18 +58,35 @@
"delete": "確定想要刪除 '__label__'?",
"dropFlowHere": "把流程放到這裡",
"addFlow": "新增流程",
"listFlows": "流程列表",
"addFlowToRight": "在右側新增流程",
"hideFlow": "隱藏流程",
"hideOtherFlows": "隱藏其它流程",
"showAllFlows": "顯示所有流程",
"hideAllFlows": "隱藏所有流程",
"hiddenFlows": "列出 __count__ 個隱藏流程",
"hiddenFlows_plural": "列出 __count__ 個隱藏流程",
"showLastHiddenFlow": "顯示最後一個隱藏流程",
" ": "流程列表",
"listSubflows": "列出子流程",
"status": "狀態",
"enabled": "有效",
"disabled": "無效",
"info": "詳細描述",
"selectNodes": "點擊節點用於選擇"
"selectNodes": "點擊節點用於選擇",
"enableFlow": "啟用流程",
"disableFlow": "禁用流程",
"lockFlow": "鎖定流程",
"unlockFlow": "解除鎖定",
"moveToStart": "移動到起始",
"moveToEnd": "移動到末尾"
},
"menu": {
"label": {
"view": {
"view": "顯示",
"grid": "格線",
"storeZoom": "加載時還原縮放尺寸",
"storePosition": "加載時還原滾動位置",
"showGrid": "顯示格線",
"snapGrid": "對齊格線",
"gridSize": "格線尺寸",
@@ -80,12 +104,14 @@
"palette": {
"show": "顯示控制板"
},
"edit": "編輯",
"settings": "設置",
"userSettings": "使用者設置",
"nodes": "節點",
"displayStatus": "顯示節點狀態",
"displayConfig": "修改節點配置",
"import": "匯入",
"importExample": "導入示例流程",
"export": "匯出",
"search": "搜尋流程",
"searchInput": "搜尋流程",
@@ -102,24 +128,48 @@
"editPalette": "節點管理",
"other": "其他",
"showTips": "顯示小提示",
"help": "Node-RED website",
"showWelcomeTours": "顯示新版本向導",
"help": "Node-RED 文檔主頁",
"projects": "專案",
"projects-new": "新專案",
"projects-open": "開啟專案",
"projects-settings": "專案設定",
"showNodeLabelDefault": "顯示新添加節點的標籤",
"codeEditor": "代碼編輯器",
"groups": "組",
"groupSelection": "選擇組",
"ungroupSelection": "取消選擇組",
"groupMergeSelection": "合并選擇",
"groupRemoveSelection": "從組中移除"
"groupRemoveSelection": "從組中移除",
"arrange": "布局",
"alignLeft": "左對齊",
"alignCenter": "居中對齊",
"alignRight": "右對齊",
"alignTop": "頂部對齊",
"alignMiddle": "垂直居中對齊",
"alignBottom": "底部對齊",
"distributeHorizontally": "横向分布",
"distributeVertically": "垂直分布",
"moveToBack": "置於底層",
"moveToFront": "置於頂層",
"moveBackwards": "向後移動",
"moveForwards": "向前移動",
"showNodeHelp":"顯示節點幫助",
"enableSelectedNodes":"啟用當前選中節點",
"disableSelectedNodes":"禁用當前選中節點",
"showSelectedNodeLabels":"顯示選中的節點標簽",
"hideSelectedNodeLabels":"隱藏選中的節點標簽"
}
},
"actions": {
"toggle-navigator": "切換導航器",
"zoom-out": "縮小",
"zoom-reset": "重置縮放",
"zoom-in": "放大"
"zoom-in": "放大",
"search-flows": "搜索流程",
"search-prev": "上一個",
"search-next": "下一個",
"search-counter": "\"__term__\" __result__ of __count__"
},
"user": {
"loggedInAs": "作為 __name__ 登入",
@@ -135,12 +185,17 @@
}
},
"notification": {
"state": {
"flowsStopped": "流程已停止",
"flowsStarted": "流程已啟動"
},
"warning": "<strong>警告</strong>: __message__",
"warnings": {
"undeployedChanges": "節點中存在未部署的更改",
"nodeActionDisabled": "節點動作在子流程中被禁用",
"nodeActionDisabledSubflow": "子流程中禁用了節點操作",
"missing-types": "流程由於缺少節點類型而停止。請檢查日誌的詳細資訊",
"missing-modules": "<p>流程因缺少模塊而停止。</p>",
"safe-mode": "<p>流程在安全模式下停止。</p><p>您可以修改流程並部署更改以重新啟動。</p>",
"restartRequired": "Node-RED必須重新啟動以啟用升級的模組",
"credentials_load_failed": "<p>流程由於無法解密證書而停止。</p> <p>流程證書文件已加密,但是項目的加密密鑰丟失或無效。</p>",
@@ -151,7 +206,7 @@
"project_not_found": "<p>找不到項目的'__project__'</p>",
"git_merge_conflict": "<p>自動合併更改失敗。</p><p>修復未合併的衝突,然後提交結果。</p>"
},
"error": "<strong>Error</strong>: __message__",
"error": "<strong>錯誤</strong>: __message__",
"errors": {
"lostConnection": "丟失與伺服器的連接,重新連接...",
"lostConnectionReconnect": "丟失與伺服器的連接__time__ 秒後重新連接",
@@ -208,6 +263,8 @@
"download": "下載",
"importUnrecognised": "匯入了無法識別的類型:",
"importUnrecognised_plural": "匯入了無法識別的類型:",
"importDuplicate": "導入了重復節點:",
"importDuplicate_plural": "導入了重復節點:",
"nodesExported": "節點匯出到了剪貼簿",
"nodesImported": "已匯入:",
"nodeCopied": "已複製 __count__ 個節點",
@@ -259,6 +316,10 @@
"modifiedFlowsDesc": "只部署包含已更改節點的流程",
"modifiedNodes": "已更改的節點",
"modifiedNodesDesc": "只部署已經更改的節點",
"startFlows": "啟動",
"startFlowsDesc": "啟動流程",
"stopFlows": "停止",
"stopFlowsDesc": "停止流程",
"restartFlows": "重新啟動流程",
"restartFlowsDesc": "重新啟動當前部署的流程",
"successfulDeploy": "部署成功",
@@ -337,14 +398,28 @@
"output": "輸出:",
"status": "狀態節點",
"deleteSubflow": "刪除子流程",
"confirmDelete": "您確定要刪除此子流程?",
"info": "詳細描述",
"category": "類別",
"module": "模塊",
"license": "許可",
"licenseNone": "無",
"licenseOther": "其它",
"type": "節點類型",
"version": "版本",
"versionPlaceholder": "x.y.z",
"keys": "關鍵字",
"keysPlaceholder": "使用英文逗號分隔關鍵字",
"author": "作者",
"authorPlaceholder": "名字 <email@example.com>",
"desc": "描述",
"env": {
"restore": "恢復為默認子流程",
"remove": "類別刪除環境變量"
},
"errors": {
"noNodesSelected": "<strong>無法創建子流程</strong>: 未選擇節點",
"acrossMultipleGroups": "無法跨多個組創建子流",
"multipleInputsToSelection": "<strong>無法創建子流程</strong>: 多個輸入到了選擇"
}
},
@@ -367,12 +442,12 @@
"editConfig": "編輯 __type__ 配置",
"addNewType": "添加新的 __type__ 節點",
"nodeProperties": "節點屬性",
"label": "Label",
"label": "標簽",
"color": "顏色",
"portLabels": "埠標籤",
"labelInputs": "輸入",
"labelOutputs": "輸出",
"settingIcon": "Icon",
"settingIcon": "圖標",
"default": "默認",
"noDefaultLabel": "無",
"defaultLabel": "使用默認標籤",
@@ -385,6 +460,7 @@
"icon": "圖標",
"inputType": "輸入類型",
"selectType": "選擇類型...",
"loadCredentials": "加載節點憑證",
"inputs": {
"input": "輸入",
"select": "選擇",
@@ -419,7 +495,8 @@
},
"errors": {
"scopeChange": "更改範圍將使其他流程中的節點無法使用",
"invalidProperties": "無效的屬性:"
"invalidProperties": "無效的屬性:",
"credentialLoadFailed": "無法加載節點憑據"
}
},
"keyboard": {
@@ -431,11 +508,14 @@
"unassigned": "未分配",
"global": "全局",
"workspace": "工作區",
"editor": "編輯對話框",
"selectAll": "選擇所有節點",
"selectNone": "取消所有選擇",
"selectAllConnected": "選擇所有連接的節點",
"addRemoveNode": "從選擇中添加/刪除節點",
"editSelected": "編輯選定節點",
"deleteSelected": "刪除選定節點或連結",
"deleteReconnect": "刪除並重新連接",
"importNode": "匯入節點",
"exportNode": "匯出節點",
"nudgeNode": "移動所選節點(1px)",
@@ -445,10 +525,14 @@
"copyNode": "複製所選節點",
"cutNode": "剪切所選節點",
"pasteNode": "粘貼節點",
"copyGroupStyle": "復製組樣式",
"pasteGroupStyle": "粘貼組樣式",
"undoChange": "撤銷上次執行的更改",
"redoChange": "重做",
"searchBox": "打開搜尋框",
"managePalette": "管理面板",
"actionList": "動作列表"
"actionList": "動作列表",
"splitWireWithLinks": "使用Link節點拆分已選項"
},
"library": {
"library": "庫",
@@ -466,12 +550,11 @@
"types": {
"local": "本地",
"examples": "例子"
},
"exportToLibrary": "將節點匯出到庫"
}
},
"palette": {
"noInfo": "無可用資訊",
"filter": "過濾節點",
"filter": "過濾已安裝模組",
"search": "搜尋模組",
"addCategory": "添加新的...",
"label": {
@@ -501,11 +584,13 @@
"nodeEnabled_plural": "啟用多個節點:",
"nodeDisabled": "禁用節點:",
"nodeDisabled_plural": "禁用多個節點:",
"nodeUpgraded": "節點模組__module__升級到__version__版本"
"nodeUpgraded": "節點模組__module__升級到__version__版本",
"unknownNodeRegistered": "加載節點錯誤: <ul><li>__type__<br>__error__</li></ul>"
},
"editor": {
"title": "面板管理",
"palette": "Palette",
"palette": "控製板",
"allCatalogs": "所有目錄",
"times": {
"seconds": "秒前",
"minutes": "分前",
@@ -545,10 +630,12 @@
"tab-nodes": "節點",
"tab-install": "安裝",
"sort": "排序:",
"sortRelevance": "關聯",
"sortAZ": "a-z順序",
"sortRecent": "日期順序",
"more": "增加 __count__ 個",
"upload": "上傳模塊tgz文件",
"refresh": "更新模塊列表",
"errors": {
"catalogLoadFailed": "無法載入節點目錄。<br>查看瀏覽器控制臺瞭解更多資訊",
"installFailed": "無法安裝: __module__<br>__message__<br>查看日誌瞭解更多資訊",
@@ -617,7 +704,11 @@
"empty": "空的",
"globalConfig": "全局配置節點",
"triggerAction": "觸發動作",
"find": "在工作區中查找"
"find": "在工作區中查找",
"copyItemUrl": "復製地址",
"copyURL2Clipboard": "復製地址到剪貼板",
"showFlow": "顯示流程",
"hideFlow": "隱藏流程"
},
"help": {
"name": "幫助",
@@ -627,7 +718,8 @@
"showHelp": "顯示幫助",
"showInOutline": "在大綱中顯示",
"showTopics": "顯示主題",
"noHelp": "未選擇幫助主題"
"noHelp": "未選擇幫助主題",
"changeLog": "更新日誌"
},
"config": {
"name": "配置節點",
@@ -828,31 +920,37 @@
"json": "JSON",
"bin": "二進位流",
"date": "時間戳記",
"jsonata": "expression",
"env": "env variable",
"jsonata": "表達式",
"env": "環境變量",
"cred": "證書"
}
},
"editableList": {
"add": "添加"
"add": "添加",
"addTitle": "添加項"
},
"search": {
"empty": "找不到匹配",
"history": "搜索歷史",
"clear": "清除所有",
"empty": "找不到匹配項",
"addNode": "添加一個節點...",
"options": {
"configNodes": "配置節點",
"unusedConfigNodes": "未使用的配置節點",
"invalidNodes": "無效的節點",
"uknownNodes": "未知的節點",
"unusedSubflows": "未使用的子流程"
"unusedSubflows": "未使用的子流程",
"hiddenFlows": "隱藏的流程",
"modifiedNodes": "已修改的節點或流程",
"thisFlow": "當前流程"
}
},
"expressionEditor": {
"functions": "功能",
"functionReference": "Function reference",
"functionReference": "功能參考",
"insert": "插入",
"title": "JSONata運算式編輯器",
"test": "Test",
"test": "測試",
"data": "示例消息",
"result": "結果",
"format": "格式表達方法",
@@ -863,20 +961,28 @@
"invalid-expr": "無效的JSONata運算式:\n __message__",
"invalid-msg": "無效的示例JSON消息:\n __message__",
"context-unsupported": "無法測試上下文函數\n $flowContext 或 $globalContext",
"env-unsupported": "無法測試 $env 函數",
"moment-unsupported": "無法測試 $moment 函數",
"clone-unsupported": "無法測試 $clone 函數",
"eval": "評估運算式錯誤:\n __message__"
}
},
"monaco": {
"setTheme": "設置主題"
},
"jsEditor": {
"title": "JavaScript 編輯器"
},
"textEditor": {
"title": "Text 編輯器"
"title": "文本編輯器"
},
"jsonEditor": {
"title": "JSON編輯器",
"format": "格式化JSON",
"rawMode": "編輯 JSON",
"uiMode": "Visual編輯器",
"uiMode": "可視化編輯器",
"rawMode-readonly": "原始JSON",
"uiMode-readonly": "可視化",
"insertAbove": "在上方插入",
"insertBelow": "在下方插入",
"addItem": "添加項目",
@@ -892,9 +998,9 @@
"title": "Markdown 編輯器",
"expand": "展開",
"format": "F使用markdown格式化",
"heading1": "Heading 1",
"heading2": "Heading 2",
"heading3": "Heading 3",
"heading1": "標題 1",
"heading2": "標題 2",
"heading3": "標題 3",
"bold": "粗體",
"italic": "斜體",
"code": "程式碼",
@@ -903,7 +1009,10 @@
"quote": "引用",
"link": "連結",
"horizontal-rule": "分隔線",
"toggle-preview": "預覽"
"toggle-preview": "切換預覽",
"mermaid": {
"summary": "美人魚圖"
}
},
"bufferEditor": {
"title": "緩衝區編輯器",
@@ -1038,7 +1147,8 @@
"not-git": "不是git倉庫",
"no-resource": "找不到存儲庫",
"cant-get-ssh-key-path": "錯誤! 無法獲取所選的SSH密鑰路徑。",
"unexpected_error": "意外的錯誤"
"unexpected_error": "意外的錯誤",
"clearContext": "更改項目時清除上下文"
},
"delete": {
"confirm": "您確定要刪除此項目嗎?"
@@ -1068,7 +1178,7 @@
"create-default-file-set": {
"no-active": "沒有活動項目就無法創建默認文件集",
"no-empty": "無法在非空項目上創建默認文件集",
"git-error": "git error"
"git-error": "git錯誤"
},
"errors": {
"no-username-email": "您的Git客戶端未配置用戶名/電子郵件。",
@@ -1079,11 +1189,20 @@
"editor-tab": {
"properties": "屬性",
"envProperties": "環境變量",
"module": "模塊屬性",
"description": "描述",
"appearance": "外觀",
"preview": "UI預覽",
"defaultValue": "默認值",
"env": "環境變量"
"defaultValue": "默認值"
},
"tourGuide": {
"takeATour": "查看更新內容",
"start": "開始",
"next": "下一個",
"welcomeTours": "歡迎使用 Node-RED"
},
"diagnostics": {
"title": "系统信息"
},
"languages": {
"de": "德語",
@@ -1095,5 +1214,31 @@
"ru":"俄語",
"zh-CN": "簡體中文",
"zh-TW": "繁體中文"
},
"validator": {
"errors": {
"invalid-json": "無效的 JSON 數據: __error__",
"invalid-expr": "無效的 JSONata 表達式: __error__",
"invalid-prop": "無效的屬性表達式",
"invalid-num": "無效的數字",
"invalid-regexp": "輸入格式無效",
"invalid-regex-prop": "__prop__: 輸入格式無效",
"missing-required-prop": "__prop__: 缺少屬性值",
"invalid-config": "__prop__: 無效的配置節點",
"missing-config": "__prop__: 缺少配置節點",
"validation-error": "__prop__: 驗證錯誤: __node__, __id__: __error__"
}
},
"contextMenu": {
"showActionList":"顯示動作列表",
"insert": "插入",
"node": "節點",
"junction": "連接點",
"linkNodes": "鏈接節點"
},
"env-var": {
"environment": "環境配置",
"header": "全局環境變量",
"revert": "重置"
}
}

View File

@@ -270,5 +270,9 @@
"$moment": {
"args": "[str]",
"desc": "使用Moment庫獲取日期對象。"
},
"$clone": {
"args": "value",
"desc": "安全克隆對象."
}
}

View File

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

View File

@@ -534,6 +534,10 @@ var RED = (function() {
RED.view.redrawStatus(node);
}
});
let pendingNodeRemovedNotifications = []
let pendingNodeRemovedTimeout
RED.comms.subscribe("notification/node/#",function(topic,msg) {
var i,m;
var typeList;
@@ -571,8 +575,15 @@ var RED = (function() {
m = msg[i];
info = RED.nodes.removeNodeSet(m.id);
if (info.added) {
typeList = "<ul><li>"+m.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeRemoved", {count:m.types.length})+typeList,"success");
pendingNodeRemovedNotifications = pendingNodeRemovedNotifications.concat(m.types.map(RED.utils.sanitize))
if (pendingNodeRemovedTimeout) {
clearTimeout(pendingNodeRemovedTimeout)
}
pendingNodeRemovedTimeout = setTimeout(function () {
typeList = "<ul><li>"+pendingNodeRemovedNotifications.join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeRemoved", {count:pendingNodeRemovedNotifications.length})+typeList,"success");
pendingNodeRemovedNotifications = []
}, 200)
}
}
loadIconList();

View File

@@ -46,6 +46,12 @@
opacity: 0.3
}).appendTo(container);
this.elementDiv.show();
if (!this.input.hasClass('red-ui-autoComplete')) {
this.input.autoComplete({
search: contextAutoComplete({ input: that }),
minLength: 0
})
}
}
var mapDeprecatedIcon = function(icon) {
if (/^red\/images\/typedInput\/.+\.png$/.test(icon)) {
@@ -54,25 +60,26 @@
return icon;
}
var autoComplete = function(options) {
function getMatch(value, searchValue) {
const idx = value.toLowerCase().indexOf(searchValue.toLowerCase());
const len = idx > -1 ? searchValue.length : 0;
return {
index: idx,
found: idx > -1,
pre: value.substring(0,idx),
match: value.substring(idx,idx+len),
post: value.substring(idx+len),
}
}
function generateSpans(match) {
const els = [];
if(match.pre) { els.push($('<span/>').text(match.pre)); }
if(match.match) { els.push($('<span/>',{style:"font-weight: bold; color: var(--red-ui-text-color-link);"}).text(match.match)); }
if(match.post) { els.push($('<span/>').text(match.post)); }
return els;
function getMatch(value, searchValue) {
const idx = value.toLowerCase().indexOf(searchValue.toLowerCase());
const len = idx > -1 ? searchValue.length : 0;
return {
index: idx,
found: idx > -1,
pre: value.substring(0,idx),
match: value.substring(idx,idx+len),
post: value.substring(idx+len),
}
}
function generateSpans(match) {
const els = [];
if(match.pre) { els.push($('<span/>').text(match.pre)); }
if(match.match) { els.push($('<span/>',{style:"font-weight: bold; color: var(--red-ui-text-color-link);"}).text(match.match)); }
if(match.post) { els.push($('<span/>').text(match.post)); }
return els;
}
const msgAutoComplete = function(options) {
return function(val) {
var matches = [];
options.forEach(opt => {
@@ -102,6 +109,196 @@
}
}
function getEnvVars (obj, envVars = {}) {
contextKnownKeys.env = contextKnownKeys.env || {}
if (contextKnownKeys.env[obj.id]) {
return contextKnownKeys.env[obj.id]
}
let parent
if (obj.type === 'tab' || obj.type === 'subflow') {
RED.nodes.eachConfig(function (conf) {
if (conf.type === "global-config") {
parent = conf;
}
})
} else if (obj.g) {
parent = RED.nodes.group(obj.g)
} else if (obj.z) {
parent = RED.nodes.workspace(obj.z) || RED.nodes.subflow(obj.z)
}
if (parent) {
getEnvVars(parent, envVars)
}
if (obj.env) {
obj.env.forEach(env => {
envVars[env.name] = obj
})
}
contextKnownKeys.env[obj.id] = envVars
return envVars
}
const envAutoComplete = function (val) {
const editStack = RED.editor.getEditStack()
if (editStack.length === 0) {
done([])
return
}
const editingNode = editStack.pop()
if (!editingNode) {
return []
}
const envVarsMap = getEnvVars(editingNode)
const envVars = Object.keys(envVarsMap)
const matches = []
const i = val.lastIndexOf('${')
let searchKey = val
let isSubkey = false
if (i > -1) {
if (val.lastIndexOf('}') < i) {
searchKey = val.substring(i+2)
isSubkey = true
}
}
envVars.forEach(v => {
let valMatch = getMatch(v, searchKey);
if (valMatch.found) {
const optSrc = envVarsMap[v]
const element = $('<div>',{style: "display: flex"});
const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"});
valEl.append(generateSpans(valMatch))
valEl.appendTo(element)
if (optSrc) {
const optEl = $('<div>').css({ "font-size": "0.8em" });
let label
if (optSrc.type === 'global-config') {
label = RED._('sidebar.context.global')
} else if (optSrc.type === 'group') {
label = RED.utils.getNodeLabel(optSrc) || (RED._('sidebar.info.group') + ': '+optSrc.id)
} else {
label = RED.utils.getNodeLabel(optSrc) || optSrc.id
}
optEl.append(generateSpans({ match: label }));
optEl.appendTo(element);
}
matches.push({
value: isSubkey ? val + v + '}' : v,
label: element,
i: valMatch.index
});
}
})
matches.sort(function(A,B){return A.i-B.i})
return matches
}
let contextKnownKeys = {}
let contextCache = {}
if (RED.events) {
RED.events.on("editor:close", function () {
contextCache = {}
contextKnownKeys = {}
});
}
const contextAutoComplete = function(options) {
const getContextKeysFromRuntime = function(scope, store, searchKey, done) {
contextKnownKeys[scope] = contextKnownKeys[scope] || {}
contextKnownKeys[scope][store] = contextKnownKeys[scope][store] || new Set()
if (searchKey.length > 0) {
try {
RED.utils.normalisePropertyExpression(searchKey)
} catch (err) {
// Not a valid context key, so don't try looking up
done()
return
}
}
const url = `context/${scope}/${encodeURIComponent(searchKey)}?store=${store}&keysOnly`
if (contextCache[url]) {
// console.log('CACHED', url)
done()
} else {
// console.log('GET', url)
$.getJSON(url, function(data) {
// console.log(data)
contextCache[url] = true
const result = data[store] || {}
const keys = result.keys || []
const keyPrefix = searchKey + (searchKey.length > 0 ? '.' : '')
keys.forEach(key => {
if (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(key)) {
contextKnownKeys[scope][store].add(keyPrefix + key)
} else {
contextKnownKeys[scope][store].add(searchKey + "[\""+key.replace(/"/,"\\\"")+"\"]")
}
})
done()
})
}
}
const getContextKeys = function(key, done) {
const keyParts = key.split('.')
const partialKey = keyParts.pop()
let scope = options.input.propertyType
if (scope === 'flow') {
// Get the flow id of the node we're editing
const editStack = RED.editor.getEditStack()
if (editStack.length === 0) {
done([])
return
}
const editingNode = editStack.pop()
if (editingNode.z) {
scope = `${scope}/${editingNode.z}`
} else {
done([])
return
}
}
const store = options.input.optionValue
const searchKey = keyParts.join('.')
getContextKeysFromRuntime(scope, store, searchKey, function() {
if (contextKnownKeys[scope][store].has(key) || key.endsWith(']')) {
getContextKeysFromRuntime(scope, store, key, function() {
done(contextKnownKeys[scope][store])
})
}
done(contextKnownKeys[scope][store])
})
}
return function(val, done) {
getContextKeys(val, function (keys) {
const matches = []
keys.forEach(v => {
let optVal = v
let valMatch = getMatch(optVal, val);
if (!valMatch.found && val.length > 0 && val.endsWith('.')) {
// Search key ends in '.' - but doesn't match. Check again
// with [" at the end instead so we match bracket notation
valMatch = getMatch(optVal, val.substring(0, val.length - 1) + '["')
}
if (valMatch.found) {
const element = $('<div>',{style: "display: flex"});
const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"});
valEl.append(generateSpans(valMatch))
valEl.appendTo(element)
matches.push({
value: optVal,
label: element,
});
}
})
matches.sort(function(a, b) { return a.value.localeCompare(b.value) });
done(matches);
})
}
}
// This is a hand-generated list of completions for the core nodes (based on the node help html).
var msgCompletions = [
{ value: "payload" },
@@ -166,7 +363,7 @@
{ value: "_session", source: ["websocket out","tcp out"] },
]
var allOptions = {
msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression, autoComplete: autoComplete(msgCompletions)},
msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression, autoComplete: msgAutoComplete(msgCompletions)},
flow: {value:"flow",label:"flow.",hasValue:true,
options:[],
validate:RED.utils.validatePropertyExpression,
@@ -251,7 +448,8 @@
env: {
value: "env",
label: "env variable",
icon: "red/images/typedInput/env.svg"
icon: "red/images/typedInput/env.svg",
autoComplete: envAutoComplete
},
node: {
value: "node",
@@ -544,7 +742,7 @@
that.element.trigger('paste',evt);
});
this.input.on('keydown', function(evt) {
if (that.typeMap[that.propertyType].autoComplete) {
if (that.typeMap[that.propertyType].autoComplete || that.input.hasClass('red-ui-autoComplete')) {
return
}
if (evt.keyCode >= 37 && evt.keyCode <= 40) {
@@ -967,6 +1165,9 @@
// If previousType is !null, then this is a change of the type, rather than the initialisation
var previousType = this.typeMap[this.propertyType];
previousValue = this.input.val();
if (this.input.hasClass('red-ui-autoComplete')) {
this.input.autoComplete("destroy");
}
if (previousType && this.typeChanged) {
if (this.options.debug) { console.log(this.identifier,"typeChanged",{previousType,previousValue}) }
@@ -1013,7 +1214,9 @@
this.input.val(this.oldValues.hasOwnProperty("_")?this.oldValues["_"]:(opt.default||""))
}
if (previousType.autoComplete) {
this.input.autoComplete("destroy");
if (this.input.hasClass('red-ui-autoComplete')) {
this.input.autoComplete("destroy");
}
}
}
this.propertyType = type;

View File

@@ -44,7 +44,7 @@ RED.contextMenu = (function () {
}
menuItems.push(
{ onselect: 'core:show-action-list', onpostselect: function () { } }
{ onselect: 'core:show-action-list', label: RED._("contextMenu.showActionList"), onpostselect: function () { } }
)
const insertOptions = []
@@ -108,16 +108,16 @@ RED.contextMenu = (function () {
const nodeOptions = []
if (!hasMultipleSelection && !isGroup) {
nodeOptions.push(
{ onselect: 'core:show-node-help' },
{ onselect: 'core:show-node-help', label: RED._('menu.label.showNodeHelp') },
null
)
}
nodeOptions.push(
{ onselect: 'core:enable-selected-nodes' },
{ onselect: 'core:disable-selected-nodes' },
{ onselect: 'core:enable-selected-nodes', label: RED._('menu.label.enableSelectedNodes') },
{ onselect: 'core:disable-selected-nodes', label: RED._('menu.label.disableSelectedNodes') },
null,
{ onselect: 'core:show-selected-node-labels' },
{ onselect: 'core:hide-selected-node-labels' }
{ onselect: 'core:show-selected-node-labels', label: RED._('menu.label.showSelectedNodeLabels') },
{ onselect: 'core:hide-selected-node-labels', label: RED._('menu.label.hideSelectedNodeLabels') }
)
menuItems.push({
label: RED._('sidebar.info.node'),
@@ -126,8 +126,8 @@ RED.contextMenu = (function () {
menuItems.push({
label: RED._('sidebar.info.group'),
options: [
{ onselect: 'core:group-selection' },
{ onselect: 'core:ungroup-selection', disabled: !hasGroup },
{ onselect: 'core:group-selection', label: RED._("menu.label.groupSelection") },
{ onselect: 'core:ungroup-selection', label: RED._("menu.label.ungroupSelection"), disabled: !hasGroup },
]
})
if (hasGroup) {
@@ -143,8 +143,8 @@ RED.contextMenu = (function () {
}
menuItems[menuItems.length - 1].options.push(
null,
{ onselect: 'core:copy-group-style', disabled: !hasGroup },
{ onselect: 'core:paste-group-style', disabled: !hasGroup}
{ onselect: 'core:copy-group-style', label: RED._("keyboard.copyGroupStyle"), disabled: !hasGroup },
{ onselect: 'core:paste-group-style', label: RED._("keyboard.pasteGroupStyle"), disabled: !hasGroup}
)
}
if (canEdit && hasMultipleSelection) {
@@ -174,7 +174,7 @@ RED.contextMenu = (function () {
{ onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !canEdit || !hasSelection },
{ onselect: 'core:copy-selection-to-internal-clipboard', label: RED._("keyboard.copyNode"), disabled: !hasSelection },
{ onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !canEdit || !RED.view.clipboard() },
{ onselect: 'core:delete-selection', disabled: !canEdit || !canDelete },
{ onselect: 'core:delete-selection', label: RED._('keyboard.deleteSelected'), disabled: !canEdit || !canDelete },
{ onselect: 'core:delete-selection-and-reconnect', label: RED._('keyboard.deleteReconnect'), disabled: !canEdit || !canDelete },
{ onselect: 'core:show-export-dialog', label: RED._("menu.label.export") },
{ onselect: 'core:select-all-nodes', label: RED._("keyboard.selectAll") },

View File

@@ -182,6 +182,17 @@ RED.editor = (function() {
error: err.message
});
}
} else if (valid) {
// 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";
const prefix = isConfig ? "node-config-input" : "node-input";
const input = $("#"+prefix+"-"+property);
const isTypedInput = input.length > 0 && input.next(".red-ui-typedInput-container").length > 0;
if (isTypedInput) {
valid = input.typedInput("validate");
}
}
}
if (valid && definition[property].type && RED.nodes.getType(definition[property].type) && !("validate" in definition[property])) {
if (!value || value == "_ADD_") {
@@ -2071,6 +2082,7 @@ RED.editor = (function() {
}
},
editBuffer: function(options) { showTypeEditor("_buffer", options) },
getEditStack: function () { return [...editStack] },
buildEditForm: buildEditForm,
validateNode: validateNode,
updateNodeProperties: updateNodeProperties,

View File

@@ -647,9 +647,9 @@ RED.sidebar.versionControl = (function() {
$.getJSON("projects/"+activeProject.name+"/commits/"+entry.sha,function(result) {
result.project = activeProject;
result.parents = entry.parents;
result.oldRev = entry.sha+"~1";
result.oldRev = entry.parents[0].length !== 0 ? entry.sha+"~1" : entry.sha;
result.newRev = entry.sha;
result.oldRevTitle = RED._("sidebar.project.versionControl.commitCapital")+" "+entry.sha.substring(0,7)+"~1";
result.oldRevTitle = entry.parents[0].length !== 0 ? RED._("sidebar.project.versionControl.commitCapital")+" "+entry.sha.substring(0,7)+"~1" : " ";
result.newRevTitle = RED._("sidebar.project.versionControl.commitCapital")+" "+entry.sha.substring(0,7);
result.date = humanizeSinceDate(parseInt(entry.date));
RED.diff.showCommitDiff(result);

View File

@@ -40,9 +40,25 @@ RED.validators = {
return opt ? RED._("validator.errors.invalid-regexp") : false;
};
},
typedInput: function(ptypeName,isConfig,mopt) {
typedInput: function(ptypeName, isConfig, mopt) {
let options = ptypeName
if (typeof ptypeName === 'string' ) {
options = {}
options.typeField = ptypeName
options.isConfig = isConfig
options.allowBlank = false
}
return function(v, opt) {
var ptype = $("#node-"+(isConfig?"config-":"")+"input-"+ptypeName).val() || this[ptypeName];
let ptype = options.type
if (!ptype && options.typeField) {
ptype = $("#node-"+(options.isConfig?"config-":"")+"input-"+options.typeField).val() || this[options.typeField];
}
if (options.allowBlank && v === '') {
return true
}
if (options.allowUndefined && v === undefined) {
return true
}
const result = RED.utils.validateTypedProperty(v, ptype, opt)
if (result === true || opt) {
// Valid, or opt provided - return result as-is

View File

@@ -114,6 +114,7 @@
pointer-events: stroke;
}
.red-ui-flow-group-outline-select {
cursor: move;
fill: none;
stroke: var(--red-ui-node-selected-color);
pointer-events: none;

View File

@@ -195,6 +195,119 @@
node.dirty = true;
});
RED.view.redraw();
},
requestDebugNodeList: function(filteredNodes) {
var workspaceOrder = RED.nodes.getWorkspaceOrder();
var workspaceOrderMap = {};
workspaceOrder.forEach(function(ws,i) {
workspaceOrderMap[ws] = i;
});
var candidateNodes = [];
var candidateSFs = [];
var subflows = {};
RED.nodes.eachNode(function (n) {
var nt = n.type;
if (nt === "debug") {
if (n.z in workspaceOrderMap) {
candidateNodes.push(n);
}
else {
var sf = RED.nodes.subflow(n.z);
if (sf) {
subflows[sf.id] = {
debug: true,
subflows: {}
};
}
}
}
else if(nt.substring(0, 8) === "subflow:") {
if (n.z in workspaceOrderMap) {
candidateSFs.push(n);
}
else {
var psf = RED.nodes.subflow(n.z);
if (psf) {
var sid = nt.substring(8);
var item = subflows[psf.id];
if (!item) {
item = {
debug: undefined,
subflows: {}
};
subflows[psf.id] = item;
}
item.subflows[sid] = true;
}
}
}
});
candidateSFs.forEach(function (sf) {
var sid = sf.type.substring(8);
if (containsDebug(sid, subflows)) {
candidateNodes.push(sf);
}
});
candidateNodes.sort(function(A,B) {
var wsA = workspaceOrderMap[A.z];
var wsB = workspaceOrderMap[B.z];
if (wsA !== wsB) {
return wsA-wsB;
}
var labelA = RED.utils.getNodeLabel(A,A.id);
var labelB = RED.utils.getNodeLabel(B,B.id);
return labelA.localeCompare(labelB);
});
var currentWs = null;
var data = [];
var currentFlow;
var currentSelectedCount = 0;
candidateNodes.forEach(function(node) {
if (currentWs !== node.z) {
if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
currentSelectedCount = 0;
currentWs = node.z;
var parent = RED.nodes.workspace(currentWs) || RED.nodes.subflow(currentWs);
currentFlow = {
label: RED.utils.getNodeLabel(parent, currentWs),
}
if (!parent.disabled) {
currentFlow.children = [];
currentFlow.checkbox = true;
} else {
currentFlow.class = "disabled"
}
data.push(currentFlow);
}
if (currentFlow.children) {
if (!filteredNodes[node.id]) {
currentSelectedCount++;
}
currentFlow.children.push({
label: RED.utils.getNodeLabel(node,node.id),
node: {
id: node.id
},
checkbox: true,
selected: !filteredNodes[node.id]
});
}
});
if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
if (subWindow) {
try {
subWindow.postMessage({event:"refreshDebugNodeList", nodes:data},"*");
} catch(err) {
console.log(err);
}
}
RED.debug.refreshDebugNodeList(data)
}
};
@@ -396,6 +509,26 @@
}
}
function containsDebug(sid, map) {
var item = map[sid];
if (item) {
if (item.debug === undefined) {
var sfs = Object.keys(item.subflows);
var contain = false;
for (var i = 0; i < sfs.length; i++) {
var sf = sfs[i];
if (containsDebug(sf, map)) {
contain = true;
break;
}
}
item.debug = contain;
}
return item.debug;
}
return false;
}
$("#red-ui-sidebar-debug-open").on("click", function(e) {
e.preventDefault();
subWindow = window.open(document.location.toString().replace(/[?#].*$/,"")+"debug/view/view.html"+document.location.search,"nodeREDDebugView","menubar=no,location=no,toolbar=no,chrome,height=500,width=600");
@@ -427,6 +560,8 @@
options.messageSourceClick(msg.id,msg._alias,msg.path);
} else if (msg.event === "clear") {
options.clear();
} else if (msg.event === "requestDebugNodeList") {
options.requestDebugNodeList(msg.filteredNodes)
}
};
window.addEventListener('message',this.handleWindowMessage);

View File

@@ -5,6 +5,7 @@ module.exports = function(RED) {
const fs = require("fs-extra");
const path = require("path");
var debuglength = RED.settings.debugMaxLength || 1000;
var statuslength = RED.settings.debugStatusLength || 32;
var useColors = RED.settings.debugUseColors || false;
util.inspect.styles.boolean = "red";
const { hasOwnProperty } = Object.prototype;
@@ -164,7 +165,7 @@ module.exports = function(RED) {
}
}
if (st.length > 32) { st = st.substr(0,32) + "..."; }
if (st.length > statuslength) { st = st.substr(0,statuslength) + "..."; }
var newStatus = {fill:fill, shape:shape, text:st};
if (JSON.stringify(newStatus) !== node.oldState) { // only send if we have to

View File

@@ -167,19 +167,13 @@ RED.debug = (function() {
var menu = RED.popover.menu({
options: options,
onselect: function(item) {
if (item.value !== filterType) {
filterType = item.value;
$('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.'+filterType));
refreshMessageList();
RED.settings.set("debug.filter",filterType)
}
setFilterType(item.value)
if (filterType === 'filterSelected') {
refreshDebugNodeList();
config.requestDebugNodeList(filteredNodes);
filterDialog.slideDown(200);
filterDialogShown = true;
debugNodeTreeList.focus();
}
}
});
menu.show({
@@ -254,131 +248,7 @@ RED.debug = (function() {
}
function containsDebug(sid, map) {
var item = map[sid];
if (item) {
if (item.debug === undefined) {
var sfs = Object.keys(item.subflows);
var contain = false;
for (var i = 0; i < sfs.length; i++) {
var sf = sfs[i];
if (containsDebug(sf, map)) {
contain = true;
break;
}
}
item.debug = contain;
}
return item.debug;
}
return false;
}
function refreshDebugNodeList() {
var workspaceOrder = RED.nodes.getWorkspaceOrder();
var workspaceOrderMap = {};
workspaceOrder.forEach(function(ws,i) {
workspaceOrderMap[ws] = i;
});
var candidateNodes = [];
var candidateSFs = [];
var subflows = {};
RED.nodes.eachNode(function (n) {
var nt = n.type;
if (nt === "debug") {
if (n.z in workspaceOrderMap) {
candidateNodes.push(n);
}
else {
var sf = RED.nodes.subflow(n.z);
if (sf) {
subflows[sf.id] = {
debug: true,
subflows: {}
};
}
}
}
else if(nt.substring(0, 8) === "subflow:") {
if (n.z in workspaceOrderMap) {
candidateSFs.push(n);
}
else {
var psf = RED.nodes.subflow(n.z);
if (psf) {
var sid = nt.substring(8);
var item = subflows[psf.id];
if (!item) {
item = {
debug: undefined,
subflows: {}
};
subflows[psf.id] = item;
}
item.subflows[sid] = true;
}
}
}
});
candidateSFs.forEach(function (sf) {
var sid = sf.type.substring(8);
if (containsDebug(sid, subflows)) {
candidateNodes.push(sf);
}
});
candidateNodes.sort(function(A,B) {
var wsA = workspaceOrderMap[A.z];
var wsB = workspaceOrderMap[B.z];
if (wsA !== wsB) {
return wsA-wsB;
}
var labelA = RED.utils.getNodeLabel(A,A.id);
var labelB = RED.utils.getNodeLabel(B,B.id);
return labelA.localeCompare(labelB);
});
var currentWs = null;
var data = [];
var currentFlow;
var currentSelectedCount = 0;
candidateNodes.forEach(function(node) {
if (currentWs !== node.z) {
if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
currentSelectedCount = 0;
currentWs = node.z;
var parent = RED.nodes.workspace(currentWs) || RED.nodes.subflow(currentWs);
currentFlow = {
label: RED.utils.getNodeLabel(parent, currentWs),
}
if (!parent.disabled) {
currentFlow.children = [];
currentFlow.checkbox = true;
} else {
currentFlow.class = "disabled"
}
data.push(currentFlow);
}
if (currentFlow.children) {
if (!filteredNodes[node.id]) {
currentSelectedCount++;
}
currentFlow.children.push({
label: RED.utils.getNodeLabel(node,node.id),
node: node,
checkbox: true,
selected: !filteredNodes[node.id]
});
}
});
if (currentFlow && currentFlow.checkbox) {
currentFlow.selected = currentSelectedCount === currentFlow.children.length
}
function refreshDebugNodeList(data) {
debugNodeTreeList.treeList("data", data);
}
@@ -401,7 +271,7 @@ RED.debug = (function() {
},200);
}
function _refreshMessageList(_activeWorkspace) {
if (_activeWorkspace) {
if (typeof _activeWorkspace === 'string') {
activeWorkspace = _activeWorkspace.replace(/\./g,"_");
}
if (filterType === "filterAll") {
@@ -479,12 +349,12 @@ RED.debug = (function() {
filteredNodes[n.id] = true;
});
delete filteredNodes[sourceId];
$("#red-ui-sidebar-debug-filterSelected").trigger("click");
RED.settings.set('debug.filteredNodes',Object.keys(filteredNodes))
setFilterType('filterSelected')
refreshMessageList();
}},
{id:"red-ui-debug-msg-menu-item-clear-filter",label:RED._("node-red:debug.messageMenu.clearFilter"),onselect:function(){
$("#red-ui-sidebar-debug-filterAll").trigger("click");
clearFilterSettings()
refreshMessageList();
}}
);
@@ -713,9 +583,17 @@ RED.debug = (function() {
if (!!clearFilter) {
clearFilterSettings();
}
refreshDebugNodeList();
config.requestDebugNodeList(filteredNodes);
}
function setFilterType(type) {
if (type !== filterType) {
filterType = type;
$('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.'+filterType));
refreshMessageList();
RED.settings.set("debug.filter",filterType)
}
}
function clearFilterSettings() {
filteredNodes = {};
filterType = 'filterAll';
@@ -728,6 +606,7 @@ RED.debug = (function() {
init: init,
refreshMessageList:refreshMessageList,
handleDebugMessage: handleDebugMessage,
clearMessageList: clearMessageList
clearMessageList: clearMessageList,
refreshDebugNodeList: refreshDebugNodeList
}
})();

View File

@@ -12,6 +12,9 @@ $(function() {
},
clear: function() {
window.opener.postMessage({event:"clear"},'*');
},
requestDebugNodeList: function(filteredNodes) {
window.opener.postMessage({event: 'requestDebugNodeList', filteredNodes},'*')
}
}
@@ -26,6 +29,8 @@ $(function() {
RED.debug.refreshMessageList(evt.data.activeWorkspace);
} else if (evt.data.event === "projectChange") {
RED.debug.clearMessageList(true);
} else if (evt.data.event === "refreshDebugNodeList") {
RED.debug.refreshDebugNodeList(evt.data.nodes)
}
},false);
} catch(err) {

View File

@@ -176,14 +176,16 @@
for (var i=0;i<rules.length;i++) {
const opt = { label: RED._('node-red:switch.label.rule')+' '+(i+1) }
const r = rules[i];
if (r.hasOwnProperty('v')) {
if ((msg = RED.utils.validateTypedProperty(r.v,r.vt,opt)) !== true) {
errors.push(msg)
if (r.t !== 'istype') {
if (r.hasOwnProperty('v')) {
if ((msg = RED.utils.validateTypedProperty(r.v,r.vt,opt)) !== true) {
errors.push(msg)
}
}
}
if (r.hasOwnProperty('v2')) {
if ((msg = RED.utils.validateTypedProperty(r.v2,r.v2t,opt)) !== true) {
errors.push(msg)
if (r.hasOwnProperty('v2')) {
if ((msg = RED.utils.validateTypedProperty(r.v2,r.v2t,opt)) !== true) {
errors.push(msg)
}
}
}
}

View File

@@ -57,7 +57,9 @@
action: {value:"scale"},
round: {value:false},
property: {value:"payload",required:true,
label:RED._("node-red:common.label.property")},
label:RED._("node-red:common.label.property"),
validate: RED.validators.typedInput({ type: 'msg', allowBlank: true })
},
name: {value:""}
},
inputs: 1,

View File

@@ -56,7 +56,7 @@
color:"darksalmon",
defaults: {
command: {value:""},
addpay: {value:""},
addpay: {value:"", validate: RED.validators.typedInput({ type: 'msg', allowBlank: true })},
append: {value:""},
useSpawn: {value:"false"},
timer: {value:""},

View File

@@ -56,9 +56,11 @@
inout: {value:"out"},
septopics: {value:true},
property: {value:"payload", required:true,
label:RED._("node-red:rbe.label.property")},
label:RED._("node-red:rbe.label.property"),
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true })},
topi: {value:"topic", required:true,
label:RED._("node-red:rbe.label.topic")}
label:RED._("node-red:rbe.label.topic"),
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true })}
},
inputs:1,
outputs:1,

View File

@@ -612,7 +612,7 @@ module.exports = function(RED) {
node.brokerurl = node.url;
} else {
// if the broker is ws:// or wss:// or tcp://
if (node.broker.indexOf("://") > -1) {
if ((typeof node.broker === 'string') && node.broker.indexOf("://") > -1) {
node.brokerurl = node.broker;
// Only for ws or wss, check if proxy env var for additional configuration
if (node.brokerurl.indexOf("wss://") > -1 || node.brokerurl.indexOf("ws://") > -1) {

View File

@@ -411,23 +411,33 @@ module.exports = function(RED) {
if (msg._session && msg._session.type == "tcp") {
var client = connectionPool[msg._session.id];
if (client) {
if (Buffer.isBuffer(msg.payload)) {
client.write(msg.payload);
} else if (typeof msg.payload === "string" && node.base64) {
client.write(Buffer.from(msg.payload,'base64'));
} else {
client.write(Buffer.from(""+msg.payload));
if (msg?.reset === true) {
client.destroy();
}
else {
if (Buffer.isBuffer(msg.payload)) {
client.write(msg.payload);
} else if (typeof msg.payload === "string" && node.base64) {
client.write(Buffer.from(msg.payload,'base64'));
} else {
client.write(Buffer.from(""+msg.payload));
}
}
}
}
else {
for (var i in connectionPool) {
if (Buffer.isBuffer(msg.payload)) {
connectionPool[i].write(msg.payload);
} else if (typeof msg.payload === "string" && node.base64) {
connectionPool[i].write(Buffer.from(msg.payload,'base64'));
} else {
connectionPool[i].write(Buffer.from(""+msg.payload));
if (msg?.reset === true) {
connectionPool[i].destroy();
}
else {
if (Buffer.isBuffer(msg.payload)) {
connectionPool[i].write(msg.payload);
} else if (typeof msg.payload === "string" && node.base64) {
connectionPool[i].write(Buffer.from(msg.payload,'base64'));
} else {
connectionPool[i].write(Buffer.from(""+msg.payload));
}
}
}
}
@@ -547,13 +557,33 @@ module.exports = function(RED) {
this.on("input", function(msg, nodeSend, nodeDone) {
var i = 0;
if ((!Buffer.isBuffer(msg.payload)) && (typeof msg.payload !== "string")) {
if (msg.payload !== undefined && (!Buffer.isBuffer(msg.payload)) && (typeof msg.payload !== "string")) {
msg.payload = msg.payload.toString();
}
var host = node.server || msg.host;
var port = node.port || msg.port;
if (node.out === "sit" && msg?.reset) {
if (msg.reset === true) { // kill all connections
for (var cl in clients) {
if (clients[cl].hasOwnProperty("client")) {
clients[cl].client.destroy();
delete clients[cl];
}
}
}
if (typeof(msg.reset) === "string" && msg.reset.includes(":")) { // just kill connection host:port
if (clients.hasOwnProperty(msg.reset) && clients[msg.reset].hasOwnProperty("client")) {
clients[msg.reset].client.destroy();
delete clients[msg.reset];
}
}
const cc = Object.keys(clients).length;
node.status({fill:"green",shape:cc===0?"ring":"dot",text:RED._("tcpin.status.connections",{count:cc})});
if ((host === undefined || port === undefined) && !msg.hasOwnProperty("payload")) { return; }
}
// Store client information independently
// the clients object will have:
// clients[id].client, clients[id].msg, clients[id].timeout
@@ -621,13 +651,16 @@ module.exports = function(RED) {
clients[connection_id].connecting = true;
clients[connection_id].client.connect(connOpts, function() {
//node.log(RED._("tcpin.errors.client-connected"));
node.status({fill:"green",shape:"dot",text:"common.status.connected"});
// node.status({fill:"green",shape:"dot",text:"common.status.connected"});
node.status({fill:"green",shape:"dot",text:RED._("tcpin.status.connections",{count:Object.keys(clients).length})});
if (clients[connection_id] && clients[connection_id].client) {
clients[connection_id].connected = true;
clients[connection_id].connecting = false;
let event;
while (event = dequeue(clients[connection_id].msgQueue)) {
clients[connection_id].client.write(event.msg.payload);
if (event.msg.payload !== undefined) {
clients[connection_id].client.write(event.msg.payload);
}
event.nodeDone();
}
if (node.out === "time" && node.splitc < 0) {
@@ -823,7 +856,9 @@ module.exports = function(RED) {
else if (!clients[connection_id].connecting && clients[connection_id].connected) {
if (clients[connection_id] && clients[connection_id].client) {
let event = dequeue(clients[connection_id].msgQueue)
clients[connection_id].client.write(event.msg.payload);
if (event.msg.payload !== undefined ) {
clients[connection_id].client.write(event.msg.payload);
}
event.nodeDone();
}
}

View File

@@ -41,8 +41,8 @@
color:"#DEBD5C",
defaults: {
name: {value:""},
property: {value:"payload"},
outproperty: {value:"payload"},
property: {value:"payload", validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true }) },
outproperty: {value:"payload", validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true }) },
tag: {value:""},
ret: {value:"html"},
as: {value:"single"}

View File

@@ -32,6 +32,7 @@
defaults: {
name: {value:""},
property: {value:"payload",required:true,
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true}),
label:RED._("node-red:json.label.property")},
action: {value:""},
pretty: {value:false}

View File

@@ -27,7 +27,8 @@
defaults: {
name: {value:""},
property: {value:"payload",required:true,
label:RED._("node-red:common.label.property")},
label:RED._("node-red:common.label.property"),
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true })},
attr: {value:""},
chr: {value:""}
},

View File

@@ -16,6 +16,7 @@
color:"#DEBD5C",
defaults: {
property: {value:"payload",required:true,
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true }),
label:RED._("node-red:common.label.property")},
name: {value:""}
},

View File

@@ -57,7 +57,7 @@
arraySplt: {value:1},
arraySpltType: {value:"len"},
stream: {value:false},
addname: {value:""}
addname: {value:"", validate: RED.validators.typedInput({ type: 'msg', allowBlank: true })}
},
inputs:1,
outputs:1,
@@ -208,7 +208,22 @@
validate:RED.validators.typedInput("propertyType", false)
},
propertyType: { value:"msg"},
key: {value:"topic"},
key: {value:"topic", validate: (function () {
const typeValidator = RED.validators.typedInput({ type: 'msg' })
return function(v, opt) {
const joinMode = $("#node-input-mode").val() || this.mode
if (joinMode !== 'custom') {
return true
}
const buildType = $("#node-input-build").val() || this.build
if (buildType !== 'object') {
return true
} else {
return typeValidator(v, opt)
}
}
})()
},
joiner: { value:"\\n"},
joinerType: { value:"str"},
accumulate: { value:"false" },

View File

@@ -198,7 +198,7 @@
category: 'storage',
defaults: {
name: {value:""},
filename: {value:""},
filename: {value:"", validate: RED.validators.typedInput({ typeField: 'filenameType' })},
filenameType: {value:"str"},
appendNewline: {value:true},
createDir: {value:false},
@@ -297,7 +297,7 @@
category: 'storage',
defaults: {
name: {value:""},
filename: {value:""},
filename: {value:"", validate: RED.validators.typedInput({ typeField: 'filenameType' }) },
filenameType: {value:"str"},
format: {value:"utf8"},
chunk: {value:false},

View File

@@ -30,6 +30,8 @@
before being sent.</p>
<p>If <code>msg._session</code> is not present the payload is
sent to <b>all</b> connected clients.</p>
<p>In Reply-to mode, setting <code>msg.reset = true</code> will reset the connection
specified by _session.id, or all connections if no _session.id is specified.</p>
<p><b>Note: </b>On some systems you may need root or administrator access
to access ports below 1024.</p>
</script>
@@ -40,6 +42,8 @@
returned characters into a fixed buffer, match a specified character before returning,
wait a fixed timeout from first reply and then return, sit and wait for data, or send then close the connection
immediately, without waiting for a reply.</p>
<p>If in sit and wait mode (remain connected) you can send <code>msg.reset = true</code> or <code>msg.reset = "host:port"</code> to force a break in
the connection and an automatic reconnection.</p>
<p>The response will be output in <code>msg.payload</code> as a buffer, so you may want to .toString() it.</p>
<p>If you leave tcp host or port blank they must be set by using the <code>msg.host</code> and <code>msg.port</code> properties in every message sent to the node.</p>
</script>

View File

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

View File

@@ -98,7 +98,7 @@ function requireModule(module) {
const parsedModule = parseModuleName(module);
if (BUILTIN_MODULES.indexOf(parsedModule.module) !== -1) {
return require(parsedModule.module);
return require(parsedModule.module + parsedModule.subpath);
}
if (!knownExternalModules[parsedModule.module]) {
const e = new Error("Module not allowed");
@@ -131,7 +131,7 @@ function importModule(module) {
const parsedModule = parseModuleName(module);
if (BUILTIN_MODULES.indexOf(parsedModule.module) !== -1) {
return import(parsedModule.module);
return import(parsedModule.module + parsedModule.subpath);
}
if (!knownExternalModules[parsedModule.module]) {
const e = new Error("Module not allowed");
@@ -152,12 +152,13 @@ function importModule(module) {
}
function parseModuleName(module) {
var match = /((?:@[^/]+\/)?[^/@]+)(?:@([\s\S]+))?/.exec(module);
var match = /((?:@[^/]+\/)?[^/@]+)(\/[^/@]+)?(?:@([\s\S]+))?/.exec(module);
if (match) {
return {
spec: module,
module: match[1],
version: match[2],
subpath: match[2] || '',
version: match[3],
builtin: BUILTIN_MODULES.indexOf(match[1]) !== -1,
known: !!knownExternalModules[match[1]]
}
@@ -263,7 +264,7 @@ async function installModule(moduleDetails) {
"module": moduleDetails.module,
"version": moduleDetails.version,
"dir": installDir,
"args": ["--production","--engine-strict"]
"args": ["--omit=dev","--engine-strict"]
}
return hooks.trigger("preInstall", triggerPayload).then((result) => {
// preInstall passed
@@ -283,6 +284,7 @@ async function installModule(moduleDetails) {
const runtimeInstalledModules = settings.get("modules") || {};
runtimeInstalledModules[moduleDetails.module] = moduleDetails;
settings.set("modules",runtimeInstalledModules)
log.audit({event: "modules.install",module:moduleDetails.module, version:moduleDetails.version});
}).catch(result => {
var output = result.stderr || result.toString();
var e;

View File

@@ -215,7 +215,7 @@ async function installModule(module,version,url) {
"dir": installDir,
"isExisting": isExisting,
"isUpgrade": isUpgrade,
"args": ['--no-audit','--no-update-notifier','--no-fund','--save','--save-prefix=~','--production','--engine-strict']
"args": ['--no-audit','--no-update-notifier','--no-fund','--save','--save-prefix=~','--omit=dev','--engine-strict']
}
return hooks.trigger("preInstall", triggerPayload).then((result) => {

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/registry",
"version": "3.1.0",
"version": "4.0.0-dev",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,7 +16,7 @@
}
],
"dependencies": {
"@node-red/util": "3.1.0",
"@node-red/util": "4.0.0-dev",
"clone": "2.1.2",
"fs-extra": "11.1.1",
"semver": "7.5.4",

View File

@@ -68,6 +68,7 @@ var api = module.exports = {
* @param {String} opts.store - the context store
* @param {String} opts.key - the context key
* @param {Object} opts.req - the request to log (optional)
* @param {Boolean} opts.keysOnly - whether to return keys only
* @return {Promise} - the node information
* @memberof @node-red/runtime_context
*/
@@ -102,6 +103,15 @@ var api = module.exports = {
if (key) {
store = store || availableStores.default;
ctx.get(key,store,function(err, v) {
if (opts.keysOnly) {
if (Array.isArray(v)) {
resolve({ [store]: { format: `array[${v.length}]`}})
} else if (typeof v === 'object') {
resolve({ [store]: { keys: Object.keys(v), format: 'Object' } })
} else {
resolve({ [store]: { keys: [] }})
}
}
var encoded = util.encodeObject({msg:v});
if (store !== availableStores.default) {
encoded.store = store;
@@ -118,32 +128,58 @@ var api = module.exports = {
stores = [store];
}
var result = {};
var c = stores.length;
var errorReported = false;
stores.forEach(function(store) {
exportContextStore(scope,ctx,store,result,function(err) {
if (err) {
// TODO: proper error reporting
if (!errorReported) {
errorReported = true;
runtime.log.audit({event: "context.get",scope:scope,id:id,store:store,key:key,error:"unexpected_error"}, opts.req);
var err = new Error();
err.code = "unexpected_error";
err.status = 400;
return reject(err);
if (opts.keysOnly) {
ctx.keys(store,function(err, keys) {
if (err) {
// TODO: proper error reporting
if (!errorReported) {
errorReported = true;
runtime.log.audit({event: "context.get",scope:scope,id:id,store:store,key:key,error:"unexpected_error"}, opts.req);
var err = new Error();
err.code = "unexpected_error";
err.status = 400;
return reject(err);
}
return
}
result[store] = { keys }
c--;
if (c === 0) {
if (!errorReported) {
runtime.log.audit({event: "context.get",scope:scope,id:id,store:store,key:key},opts.req);
resolve(result);
}
}
})
} else {
exportContextStore(scope,ctx,store,result,function(err) {
if (err) {
// TODO: proper error reporting
if (!errorReported) {
errorReported = true;
runtime.log.audit({event: "context.get",scope:scope,id:id,store:store,key:key,error:"unexpected_error"}, opts.req);
var err = new Error();
err.code = "unexpected_error";
err.status = 400;
return reject(err);
}
return;
}
c--;
if (c === 0) {
if (!errorReported) {
runtime.log.audit({event: "context.get",scope:scope,id:id,store:store,key:key},opts.req);
resolve(result);
return;
}
}
});
c--;
if (c === 0) {
if (!errorReported) {
runtime.log.audit({event: "context.get",scope:scope,id:id,store:store,key:key},opts.req);
resolve(result);
}
}
});
}
})
}
} else {

View File

@@ -112,6 +112,7 @@ class Subflow extends Flow {
remapSubflowNodes(subflowInternalFlowConfig.configs,node_map);
remapSubflowNodes(subflowInternalFlowConfig.nodes,node_map);
remapSubflowNodes(subflowInternalFlowConfig.groups,node_map);
// console.log("Instance config\n",JSON.stringify(subflowInternalFlowConfig,"",2));
@@ -248,7 +249,7 @@ class Subflow extends Flow {
for (j=0;j<wires.length;j++) {
if (wires[j].id != self.subflowDef.id) {
node = self.node_map[wires[j].id];
if (node._originalWires) {
if (node && node._originalWires) {
node.wires = clone(node._originalWires);
}
}
@@ -265,8 +266,10 @@ class Subflow extends Flow {
subflowInstanceModified = true;
} else {
node = self.node_map[wires[j].id];
node.wires[wires[j].port] = node.wires[wires[j].port].concat(newWires[i]);
modifiedNodes[node.id] = node;
if (node) {
node.wires[wires[j].port] = node.wires[wires[j].port].concat(newWires[i]);
modifiedNodes[node.id] = node;
}
}
}
}
@@ -294,10 +297,14 @@ class Subflow extends Flow {
this.node._updateWires(subflowInstanceConfig.wires);
} else {
var node = self.node_map[wires[j].id];
if (!node._originalWires) {
node._originalWires = clone(node.wires);
if (node) {
if (!node._originalWires) {
node._originalWires = clone(node.wires);
}
node.wires[wires[j].port] = (node.wires[wires[j].port]||[]).concat(this.subflowInstance.wires[i]);
} else {
this.error("Unknown node referenced inside subflow: " + wires[j].id)
}
node.wires[wires[j].port] = (node.wires[wires[j].port]||[]).concat(this.subflowInstance.wires[i]);
}
}
}
@@ -313,11 +320,15 @@ class Subflow extends Flow {
this.node._updateWires(subflowInstanceConfig.wires);
} else {
var node = self.node_map[wires[j].id];
if (!node._originalWires) {
node._originalWires = clone(node.wires);
if (node) {
if (!node._originalWires) {
node._originalWires = clone(node.wires);
}
node.wires[wires[j].port] = (node.wires[wires[j].port]||[]);
node.wires[wires[j].port].push(subflowStatusId);
} else {
this.error("Unknown node referenced inside subflow: " + wires[j].id)
}
node.wires[wires[j].port] = (node.wires[wires[j].port]||[]);
node.wires[wires[j].port].push(subflowStatusId);
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/runtime",
"version": "3.1.0",
"version": "4.0.0-dev",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,8 +16,8 @@
}
],
"dependencies": {
"@node-red/registry": "3.1.0",
"@node-red/util": "3.1.0",
"@node-red/registry": "4.0.0-dev",
"@node-red/util": "4.0.0-dev",
"async-mutex": "0.4.0",
"clone": "2.1.2",
"express": "4.18.2",

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "3.1.0",
"version": "4.0.0-dev",
"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": "3.1.0",
"@node-red/runtime": "3.1.0",
"@node-red/util": "3.1.0",
"@node-red/nodes": "3.1.0",
"@node-red/editor-api": "4.0.0-dev",
"@node-red/runtime": "4.0.0-dev",
"@node-red/util": "4.0.0-dev",
"@node-red/nodes": "4.0.0-dev",
"basic-auth": "2.0.1",
"bcryptjs": "2.4.3",
"express": "4.18.2",

View File

@@ -449,6 +449,7 @@ module.exports = {
* - ui (for use with Node-RED Dashboard)
* - debugUseColors
* - debugMaxLength
* - debugStatusLength
* - execMaxBufferSize
* - httpRequestTimeout
* - mqttReconnectTime
@@ -504,6 +505,9 @@ module.exports = {
/** The maximum length, in characters, of any message sent to the debug sidebar tab */
debugMaxLength: 1000,
/** The maximum length, in characters, of status messages under the debug node */
//debugStatusLength: 32,
/** Maximum buffer size for the exec node. Defaults to 10Mb */
//execMaxBufferSize: 10000000,

View File

@@ -1718,9 +1718,13 @@ describe('function node', function() {
describe("init function", function() {
it('should delay handling messages until init completes', function(done) {
const timeoutMS = 200;
// Since helper.load uses process.nextTick timers might occasionally finish
// a couple of milliseconds too early, so give some leeway to the check.
const timeoutCheckMargin = 5;
var flow = [{id:"n1",type:"function",wires:[["n2"]],initialize: `
return new Promise((resolve,reject) => {
setTimeout(resolve,200)
setTimeout(resolve, ${timeoutMS});
})`,
func:"return msg;"
},
@@ -1733,9 +1737,10 @@ describe('function node', function() {
msg.delta = Date.now() - msg.payload;
receivedMsgs.push(msg)
if (receivedMsgs.length === 5) {
var errors = receivedMsgs.filter(msg => msg.delta < 200)
let deltas = receivedMsgs.map(msg => msg.delta);
var errors = deltas.filter(delta => delta < (timeoutMS - timeoutCheckMargin))
if (errors.length > 0) {
done(new Error(`Message received before init completed - was ${msg.delta} expected >300`))
done(new Error(`Message received before init completed - delta values ${JSON.stringify(deltas)} expected to be > ${timeoutMS - timeoutCheckMargin}`))
} else {
done();
}