node-red-node-defaults 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +7 -0
- package/index.js +14 -0
- package/package.json +161 -0
- package/plugin.html +71 -0
- package/readme.md +102 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 William Shostak
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module.exports = function(RED) {
|
|
2
|
+
const nodeDefaults = RED.settings.nodeDefaults || {}
|
|
3
|
+
|
|
4
|
+
// Expose nodeDefaults to the editor via HTTP.
|
|
5
|
+
// RED.settings with exportable:true uses a different client-side path;
|
|
6
|
+
// a plain GET endpoint is the reliable approach (see SFL pattern).
|
|
7
|
+
RED.httpAdmin.get('/node-defaults/config', (req, res) => {
|
|
8
|
+
res.json(nodeDefaults)
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
RED.plugins.registerPlugin('node-red-node-defaults', {
|
|
12
|
+
type: 'node-defaults'
|
|
13
|
+
})
|
|
14
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "node-red-node-defaults",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Node-RED editor plugin that injects default values into node instances when dropped onto the canvas",
|
|
5
|
+
"author": "Will Shostak",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"node-red",
|
|
8
|
+
"node-red-plugin",
|
|
9
|
+
"node-red-defaults",
|
|
10
|
+
"node-red-editor"
|
|
11
|
+
],
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"private": false,
|
|
14
|
+
"scripts": {
|
|
15
|
+
"dev:prepare": "husky install",
|
|
16
|
+
"coverage": "jest --coverage",
|
|
17
|
+
"test": "jest",
|
|
18
|
+
"test:debug": "jest --detectOpenHandles"
|
|
19
|
+
},
|
|
20
|
+
"node-red": {
|
|
21
|
+
"plugins": {
|
|
22
|
+
"node-red-node-defaults": "plugin.html"
|
|
23
|
+
},
|
|
24
|
+
"nodes": {
|
|
25
|
+
"node-red-node-defaults": "index.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"jest": {
|
|
29
|
+
"verbose": true,
|
|
30
|
+
"testMatch": [
|
|
31
|
+
"**/test/*.js?(x)"
|
|
32
|
+
],
|
|
33
|
+
"reporters": [
|
|
34
|
+
"default",
|
|
35
|
+
[
|
|
36
|
+
"jest-junit",
|
|
37
|
+
{
|
|
38
|
+
"outputDirectory": ".",
|
|
39
|
+
"outputName": "test-results.xml"
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
],
|
|
43
|
+
"coverageReporters": [
|
|
44
|
+
"cobertura",
|
|
45
|
+
"text"
|
|
46
|
+
],
|
|
47
|
+
"transform": {
|
|
48
|
+
"^.+\\.jsx?$": "babel-jest"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"eslintConfig": {
|
|
52
|
+
"parserOptions": {
|
|
53
|
+
"sourceType": "script",
|
|
54
|
+
"ecmaVersion": 2020,
|
|
55
|
+
"ecmaFeatures": {
|
|
56
|
+
"globalReturn": true
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"env": {
|
|
60
|
+
"es2021": true,
|
|
61
|
+
"node": true,
|
|
62
|
+
"browser": true,
|
|
63
|
+
"commonjs": true,
|
|
64
|
+
"jest": true
|
|
65
|
+
},
|
|
66
|
+
"extends": [
|
|
67
|
+
"airbnb-base",
|
|
68
|
+
"plugin:cypress/recommended",
|
|
69
|
+
"plugin:chai-friendly/recommended"
|
|
70
|
+
],
|
|
71
|
+
"plugins": [
|
|
72
|
+
"node",
|
|
73
|
+
"security",
|
|
74
|
+
"prettier"
|
|
75
|
+
],
|
|
76
|
+
"rules": {
|
|
77
|
+
"curly": [
|
|
78
|
+
"error",
|
|
79
|
+
"all"
|
|
80
|
+
],
|
|
81
|
+
"semi": [
|
|
82
|
+
"error",
|
|
83
|
+
"never"
|
|
84
|
+
],
|
|
85
|
+
"comma-dangle": [
|
|
86
|
+
"error",
|
|
87
|
+
"never"
|
|
88
|
+
],
|
|
89
|
+
"arrow-parens": [
|
|
90
|
+
"error",
|
|
91
|
+
"as-needed"
|
|
92
|
+
],
|
|
93
|
+
"sort-imports": [
|
|
94
|
+
"error",
|
|
95
|
+
{
|
|
96
|
+
"ignoreMemberSort": true,
|
|
97
|
+
"ignoreDeclarationSort": true
|
|
98
|
+
}
|
|
99
|
+
],
|
|
100
|
+
"no-param-reassign": [
|
|
101
|
+
"error",
|
|
102
|
+
{
|
|
103
|
+
"props": false
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
"no-plusplus": [
|
|
107
|
+
"error",
|
|
108
|
+
{
|
|
109
|
+
"allowForLoopAfterthoughts": true
|
|
110
|
+
}
|
|
111
|
+
],
|
|
112
|
+
"no-bitwise": [
|
|
113
|
+
"error",
|
|
114
|
+
{
|
|
115
|
+
"allow": [
|
|
116
|
+
"~"
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
],
|
|
120
|
+
"jest/no-disabled-tests": "warn",
|
|
121
|
+
"jest/no-focused-tests": "error",
|
|
122
|
+
"jest/no-identical-title": "error",
|
|
123
|
+
"jest/prefer-to-have-length": "warn",
|
|
124
|
+
"jest/valid-expect": "error"
|
|
125
|
+
},
|
|
126
|
+
"globals": {
|
|
127
|
+
"logger": true,
|
|
128
|
+
"kantan": true,
|
|
129
|
+
"logToDB": true,
|
|
130
|
+
"configs": true,
|
|
131
|
+
"msg": true
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
"dependencies": {},
|
|
135
|
+
"optionalDependencies": {},
|
|
136
|
+
"devDependencies": {
|
|
137
|
+
"jest": "^29.0.0",
|
|
138
|
+
"concurrently": "^7.2.2",
|
|
139
|
+
"cross-env": "^7.0.3",
|
|
140
|
+
"eslint": "^8.57.1",
|
|
141
|
+
"eslint-config-airbnb-base": "^14.1.0",
|
|
142
|
+
"eslint-plugin-import": "^2.20.2",
|
|
143
|
+
"eslint-plugin-jest": "^28.11.0",
|
|
144
|
+
"eslint-plugin-node": "^11.1.0",
|
|
145
|
+
"eslint-plugin-prettier": "^3.3.0",
|
|
146
|
+
"eslint-plugin-security": "^1.4.0",
|
|
147
|
+
"husky": "8.0.3",
|
|
148
|
+
"jest-junit": "^16.0.0",
|
|
149
|
+
"node-red": "^4.0.0",
|
|
150
|
+
"node-red-node-test-helper": "^0.3.5",
|
|
151
|
+
"nyc": "^15.1.0",
|
|
152
|
+
"prettier": "^2.2.1",
|
|
153
|
+
"prettier-eslint": "^12.0.0",
|
|
154
|
+
"wait-on": "^6.0.1"
|
|
155
|
+
},
|
|
156
|
+
"browserslist": [
|
|
157
|
+
"> 1%",
|
|
158
|
+
"last 2 versions",
|
|
159
|
+
"not dead"
|
|
160
|
+
]
|
|
161
|
+
}
|
package/plugin.html
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
// Object-first module: injection logic in a named object so it mirrors
|
|
3
|
+
// node-defaults-injector.js (the testable CommonJS twin of this browser code).
|
|
4
|
+
// Injection rules:
|
|
5
|
+
// 1. field is null / undefined / empty string → inject
|
|
6
|
+
// 2. field matches the type's built-in default → inject (never changed)
|
|
7
|
+
// 3. field differs from built-in default → leave (user set it)
|
|
8
|
+
// typeDef.defaults[field].value is the stable built-in default — never mutated.
|
|
9
|
+
// Credential fields (typeDef.credentials) use rule 1 only.
|
|
10
|
+
const NodeDefaults = {
|
|
11
|
+
injectIntoNode(node, configFields, typeDef) {
|
|
12
|
+
Object.entries(configFields).forEach(([field, configValue]) => {
|
|
13
|
+
const { defaults = {}, credentials = {} } = typeDef
|
|
14
|
+
const inDefaults = field in defaults
|
|
15
|
+
const inCredentials = field in credentials
|
|
16
|
+
|
|
17
|
+
if (inCredentials) {
|
|
18
|
+
// Credentials live on node.credentials — inject only when empty.
|
|
19
|
+
node.credentials = node.credentials || {}
|
|
20
|
+
const currentValue = node.credentials[field]
|
|
21
|
+
|
|
22
|
+
if (currentValue == null || currentValue === '') {
|
|
23
|
+
node.credentials[field] = configValue
|
|
24
|
+
}
|
|
25
|
+
} else if (inDefaults) {
|
|
26
|
+
// Regular defaults — three-rule injection.
|
|
27
|
+
const builtinDefault = defaults[field].value
|
|
28
|
+
const currentValue = node[field]
|
|
29
|
+
const isUnchanged = currentValue === builtinDefault
|
|
30
|
+
|
|
31
|
+
if (currentValue == null || currentValue === '' || isUnchanged) {
|
|
32
|
+
node[field] = configValue
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
RED.plugins.registerPlugin('node-red-node-defaults', {
|
|
40
|
+
type: 'node-defaults',
|
|
41
|
+
onadd() {
|
|
42
|
+
const adminRoot = (RED.settings.httpAdminRoot || '').replace(/\/$/, '')
|
|
43
|
+
const url = `${adminRoot}/node-defaults/config`
|
|
44
|
+
|
|
45
|
+
// Safe default — {} means nodeDefaults[node.type] is cleanly falsy until
|
|
46
|
+
// the fetch resolves. No undefined in the model.
|
|
47
|
+
let nodeDefaults = {}
|
|
48
|
+
|
|
49
|
+
RED.events.on('nodes:add', node => {
|
|
50
|
+
if (node && nodeDefaults[node.type]) {
|
|
51
|
+
const typeDef = RED.nodes.getType(node.type)
|
|
52
|
+
|
|
53
|
+
if (typeDef) {
|
|
54
|
+
NodeDefaults.injectIntoNode(node, nodeDefaults[node.type], typeDef)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
$.getJSON(url).done(data => {
|
|
60
|
+
if (!data || Object.keys(data).length === 0) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
nodeDefaults = data
|
|
65
|
+
})
|
|
66
|
+
.fail(err => {
|
|
67
|
+
console.error('[node-red-node-defaults] failed to fetch config', err)
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
</script>
|
package/readme.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# node-red-node-defaults
|
|
2
|
+
|
|
3
|
+
A Node-RED editor plugin that automatically injects default values into node instances when they are dropped onto the canvas.
|
|
4
|
+
|
|
5
|
+
Defaults are defined once in `settings.js` and apply to every new node of the configured type — no flow-by-flow setup required.
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
The plugin has two parts:
|
|
10
|
+
|
|
11
|
+
| Part | File | Role |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| Server | `index.js` | Reads `nodeDefaults` from `settings.js`, exposes it via a lightweight HTTP endpoint |
|
|
14
|
+
| Editor | `index.html` | Fetches the config on load, listens for `nodes:add`, and injects defaults into each new node instance |
|
|
15
|
+
|
|
16
|
+
### Injection rules (per field)
|
|
17
|
+
|
|
18
|
+
For each configured field the editor applies these rules in order:
|
|
19
|
+
|
|
20
|
+
1. **Field is empty** (`null`, `undefined`, `''`) → inject
|
|
21
|
+
2. **Field matches the node type's built-in default** → inject (user never changed it)
|
|
22
|
+
3. **Field differs from the built-in default** → leave (user set it intentionally)
|
|
23
|
+
|
|
24
|
+
Credential fields (`userid`, `password`, etc.) use rule 1 only — inject if empty, leave if already set.
|
|
25
|
+
|
|
26
|
+
Existing saved nodes in a flow are never overwritten — injection only applies to nodes added to the canvas after the plugin loads.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# In your Node-RED user directory (typically ~/.node-red)
|
|
32
|
+
npm install node-red-node-defaults
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or during development, link it:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# In this package directory
|
|
39
|
+
yarn link
|
|
40
|
+
|
|
41
|
+
# In your Node-RED user directory
|
|
42
|
+
yarn link node-red-node-defaults
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Configuration
|
|
46
|
+
|
|
47
|
+
Add a `nodeDefaults` object to your Node-RED `settings.js`. The top-level keys are node type names exactly as registered (e.g. `'e-mail'`, not the package name).
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
// settings.js
|
|
51
|
+
module.exports = {
|
|
52
|
+
// ... other settings ...
|
|
53
|
+
|
|
54
|
+
nodeDefaults: {
|
|
55
|
+
// Outbound SMTP (node-red-node-email)
|
|
56
|
+
'e-mail': {
|
|
57
|
+
server: 'smtp.example.com',
|
|
58
|
+
port: '587',
|
|
59
|
+
secure: false, // false = STARTTLS on 587; true = TLS on 465
|
|
60
|
+
tls: false,
|
|
61
|
+
authtype: 'BASIC',
|
|
62
|
+
userid: 'noreply@example.com', // credential field
|
|
63
|
+
password: 'yourpassword' // credential field
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
// Inbound IMAP/POP3 (node-red-node-email)
|
|
67
|
+
'e-mail in': {
|
|
68
|
+
protocol: 'IMAP',
|
|
69
|
+
server: 'imap.example.com',
|
|
70
|
+
port: '993',
|
|
71
|
+
useSSL: true,
|
|
72
|
+
autotls: 'never', // 'never' | 'required' | 'always'
|
|
73
|
+
box: 'INBOX',
|
|
74
|
+
disposition: 'Read', // 'None' | 'Read' | 'Delete'
|
|
75
|
+
criteria: 'UNSEEN',
|
|
76
|
+
repeat: '300'
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Finding the correct field names
|
|
83
|
+
|
|
84
|
+
Field names must match what the node type declares in its `defaults` or `credentials` block. The easiest way to find them:
|
|
85
|
+
|
|
86
|
+
1. Open the Node-RED editor
|
|
87
|
+
2. Drop the node onto the canvas
|
|
88
|
+
3. Open **Browser DevTools → Network → `/nodes`**
|
|
89
|
+
4. Find the node's HTML file and look at `RED.nodes.registerType('type-name', { defaults: { ... }, credentials: { ... } })`
|
|
90
|
+
|
|
91
|
+
## Security note
|
|
92
|
+
|
|
93
|
+
Credential values (e.g. `userid`, `password`) set in `settings.js` are returned in plaintext from the `/node-defaults/config` endpoint to any authenticated browser session that can reach the Node-RED editor. Ensure your Node-RED instance is not publicly accessible when using credential defaults.
|
|
94
|
+
|
|
95
|
+
### Contact
|
|
96
|
+
|
|
97
|
+
- **Author:** William Shostak (https://github.com/wshostak)
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
This project is licensed under the **ISC License** — see the **[LICENSE](./LICENSE.txt)** file for more details.
|
|
101
|
+
|
|
102
|
+
Copyright (c) 2026 William Shostak
|