├── .eslintrc.js
├── .gitattributes
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── examples
└── simple-test.json
├── gulpfile.js
├── index.d.ts
├── nodes
├── jktest-server.html
├── jktest-server.js
├── jktest.html
├── jktest.js
├── jktest2.html
├── jktest2.js
├── jktestEditableList.html
└── jktestEditableList.js
├── package-lock.json
├── package.json
└── typedefs.js
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable strict */
2 | /** JavaScript Versions
3 | * 5 is minimum -> Last IE11
4 | * 6 = 2015 -> Node >8.10, iOS12+
5 | * 7 = 2016 -> FF78+,
6 | * 8 = 2017 -> Node 10.9+
7 | * 9 = 2018 -> Node 12.11+
8 | * 10 = 2019
9 | * 11 = 2020
10 | * 12 = 2021
11 | */
12 | module.exports = {
13 | env: {
14 | browser: false,
15 | commonjs: true,
16 | jquery: false,
17 | node: true,
18 | 'shared-node-browser': false
19 | },
20 | parserOptions: {
21 | ecmaVersion: 2018,
22 | sourceType: 'script'
23 | },
24 | root: true,
25 | globals: {
26 | Set: true, // Not sure why eslint doesn't recognise this as it is part of node.js since v0.12
27 | },
28 | plugins: [
29 | 'html', // Check scripts in HTML. https://www.npmjs.com/package/eslint-plugin-html
30 | 'es', // Help avoid js that is too new. https://eslint-plugin-es.mysticatea.dev/
31 | 'jsdoc', // JSDoc. https://www.npmjs.com/package/eslint-plugin-jsdoc
32 | 'promise', // Better promises. https://www.npmjs.com/package/eslint-plugin-promise
33 | 'sonarjs' // Detect bugs and suspicious patterns. https://github.com/SonarSource/eslint-plugin-sonarjs
34 | //'prettier', // https://www.npmjs.com/package/eslint-plugin-prettier
35 | ],
36 | extends: [
37 | 'eslint:recommended',
38 | 'plugin:es/restrict-to-es2018',
39 | 'plugin:jsdoc/recommended',
40 | 'plugin:promise/recommended',
41 | 'plugin:sonarjs/recommended',
42 | //'plugin:prettier/recommended',
43 | ],
44 | // settings: {
45 | // jsdoc: {
46 | // mode: 'permissive'
47 | // }
48 | // },
49 | rules: {
50 | 'jsdoc/multiline-blocks': 0,
51 | 'jsdoc/newline-after-description': 0,
52 | 'jsdoc/no-multi-asterisks': 0,
53 | 'jsdoc/tag-lines': 0,
54 | 'jsdoc/no-undefined-types': ['error'|'warn', {'definedTypes':['Promise']}],
55 |
56 | 'sonarjs/cognitive-complexity': ['error', 50], // default is 15! Need to try and improve this :-)
57 |
58 | quotes: [
59 | 'warn',
60 | 'single'
61 | ],
62 |
63 | 'accessor-pairs': 'error',
64 | 'array-bracket-newline': 'error',
65 | 'array-bracket-spacing': 0,
66 | 'array-callback-return': 'off',
67 | 'array-element-newline': 'off',
68 | 'arrow-body-style': 'error',
69 | 'arrow-parens': 'off',
70 | 'arrow-spacing': [
71 | 'error',
72 | {
73 | after: true,
74 | before: true
75 | }
76 | ],
77 | 'block-scoped-var': 'error',
78 | 'block-spacing': [
79 | 'error',
80 | 'always'
81 | ],
82 | 'brace-style': [
83 | 'error',
84 | '1tbs',
85 | {
86 | allowSingleLine: true
87 | }
88 | ],
89 | 'callback-return': 'error',
90 | camelcase: 'off',
91 | 'capitalized-comments': 'off',
92 | 'class-methods-use-this': 'error',
93 | 'comma-dangle': 'off',
94 | 'comma-spacing': 'off',
95 | 'comma-style': [
96 | 'error',
97 | 'last'
98 | ],
99 | complexity: 'off',
100 | 'computed-property-spacing': [
101 | 'error',
102 | 'never'
103 | ],
104 | 'consistent-return': 'off',
105 | 'consistent-this': 'off',
106 | curly: 'off',
107 | 'default-case': 'off',
108 | 'default-case-last': 'error',
109 | 'default-param-last': 'error',
110 | 'dot-location': [
111 | 'error',
112 | 'property'
113 | ],
114 | 'dot-notation': [
115 | 'error',
116 | {
117 | allowKeywords: true
118 | }
119 | ],
120 | 'eol-last': 'error',
121 | eqeqeq: 'error',
122 | 'func-call-spacing': 'error',
123 | 'func-name-matching': 'error',
124 | 'func-names': 'off',
125 | 'func-style': 'off',
126 | 'function-call-argument-newline': 'off',
127 | 'function-paren-newline': 'off',
128 | 'generator-star-spacing': 'error',
129 | 'global-require': 'off',
130 | 'grouped-accessor-pairs': 'error',
131 | 'guard-for-in': 'error',
132 | 'handle-callback-err': 'error',
133 | 'id-blacklist': 'error',
134 | 'id-denylist': 'error',
135 | 'id-length': 'off',
136 | 'id-match': 'error',
137 | 'implicit-arrow-linebreak': [
138 | 'error',
139 | 'beside'
140 | ],
141 | indent: [
142 | 'warn',
143 | 4,
144 | {'SwitchCase': 1}
145 | ],
146 | 'indent-legacy': 'off',
147 | 'init-declarations': 'off',
148 | 'jsx-quotes': 'error',
149 | 'key-spacing': 'off',
150 | 'keyword-spacing': 'off',
151 | 'line-comment-position': 'off',
152 | 'linebreak-style': [
153 | 'error',
154 | 'unix'
155 | ],
156 | 'lines-around-comment': 'off',
157 | 'lines-around-directive': 'off',
158 | 'lines-between-class-members': 'error',
159 | 'max-classes-per-file': 'error',
160 | 'max-depth': 'error',
161 | 'max-len': 'off',
162 | 'max-lines': 'off',
163 | 'max-lines-per-function': 'off',
164 | 'max-nested-callbacks': 'error',
165 | 'max-params': ['error', 6],
166 | 'max-statements': 'off',
167 | 'max-statements-per-line': 'off',
168 | 'multiline-comment-style': 'off',
169 | 'new-cap': 'error',
170 | 'new-parens': 'error',
171 | 'newline-after-var': 'off',
172 | 'newline-before-return': 'off',
173 | 'newline-per-chained-call': 'error',
174 | 'no-alert': 'error',
175 | 'no-array-constructor': 'error',
176 | 'no-await-in-loop': 'error',
177 | 'no-bitwise': 'off',
178 | 'no-buffer-constructor': 'error',
179 | 'no-caller': 'error',
180 | 'no-catch-shadow': 'error',
181 | 'no-confusing-arrow': 'error',
182 | 'no-console': 'off',
183 | 'no-constructor-return': 'error',
184 | 'no-continue': 'error',
185 | 'no-div-regex': 'error',
186 | 'no-duplicate-imports': 'error',
187 | 'no-else-return': [
188 | 'error',
189 | {
190 | allowElseIf: true
191 | }
192 | ],
193 | 'no-empty-function': 'off',
194 | 'no-eq-null': 'error',
195 | 'no-eval': 'error',
196 | 'no-extend-native': 'error',
197 | 'no-extra-bind': 'error',
198 | 'no-extra-label': 'error',
199 | 'no-extra-parens': 'off',
200 | 'no-floating-decimal': 'error',
201 | 'no-implicit-coercion': 'error',
202 | 'no-implicit-globals': 'error',
203 | 'no-implied-eval': 'error',
204 | 'no-inline-comments': 'off',
205 | 'no-inner-declarations': [
206 | 'error',
207 | 'functions'
208 | ],
209 | 'no-invalid-this': 'off',
210 | 'no-iterator': 'error',
211 | 'no-label-var': 'error',
212 | 'no-labels': 'error',
213 | 'no-lone-blocks': 'error',
214 | 'no-lonely-if': 'error',
215 | 'no-loop-func': 'error',
216 | 'no-loss-of-precision': 'error',
217 | 'no-magic-numbers': 'off',
218 | 'no-mixed-operators': 'error',
219 | 'no-mixed-requires': 'error',
220 | 'no-multi-assign': 'off',
221 | 'no-multi-spaces': 'off',
222 | 'no-multi-str': 'error',
223 | 'no-multiple-empty-lines': 'error',
224 | 'no-native-reassign': 'error',
225 | 'no-negated-condition': 'off',
226 | 'no-negated-in-lhs': 'error',
227 | 'no-nested-ternary': 'error',
228 | 'no-new': 'error',
229 | 'no-new-func': 'error',
230 | 'no-new-object': 'error',
231 | 'no-new-require': 'error',
232 | 'no-new-wrappers': 'error',
233 | 'no-nonoctal-decimal-escape': 'error',
234 | 'no-octal-escape': 'error',
235 | 'no-param-reassign': 'off',
236 | 'no-path-concat': 'error',
237 | 'no-plusplus': 'off',
238 | 'no-process-env': 'off',
239 | 'no-process-exit': 'error',
240 | 'no-promise-executor-return': 'error',
241 | 'no-proto': 'error',
242 | 'no-restricted-exports': 'error',
243 | 'no-restricted-globals': 'error',
244 | 'no-restricted-imports': 'error',
245 | 'no-restricted-modules': 'error',
246 | 'no-restricted-properties': 'error',
247 | 'no-restricted-syntax': 'error',
248 | 'no-return-assign': 'error',
249 | 'no-return-await': 'error',
250 | 'no-script-url': 'error',
251 | 'no-self-compare': 'error',
252 | 'no-sequences': 'error',
253 | 'no-shadow': 'off',
254 | 'no-spaced-func': 'error',
255 | 'no-sync': 'off',
256 | 'no-tabs': 'error',
257 | 'no-template-curly-in-string': 'error',
258 | 'no-ternary': 'off',
259 | 'no-throw-literal': 'error',
260 | 'no-trailing-spaces': 'off',
261 | 'no-undef-init': 'error',
262 | 'no-undefined': 'off',
263 | 'no-underscore-dangle': 'off',
264 | 'no-unmodified-loop-condition': 'error',
265 | 'no-unneeded-ternary': 'off',
266 | 'no-unreachable-loop': 'error',
267 | 'no-unsafe-optional-chaining': 'error',
268 | 'no-use-before-define': 'error',
269 | 'no-useless-backreference': 'error',
270 | 'no-useless-call': 'error',
271 | 'no-useless-computed-key': 'error',
272 | 'no-useless-concat': 'error',
273 | 'no-useless-constructor': 'error',
274 | 'no-useless-rename': 'error',
275 | 'no-useless-return': 'off',
276 | 'no-var': 'off',
277 | 'no-void': 'error',
278 | 'no-warning-comments': 'off',
279 | 'no-whitespace-before-property': 'error',
280 | 'nonblock-statement-body-position': [
281 | 'error',
282 | 'any'
283 | ],
284 | 'object-curly-newline': 'error',
285 | 'object-curly-spacing': 'off',
286 | 'object-shorthand': 'off',
287 | 'one-var': 'off',
288 | 'one-var-declaration-per-line': 'off',
289 | 'operator-assignment': [
290 | 'error',
291 | 'always'
292 | ],
293 | 'operator-linebreak': 'error',
294 | 'padded-blocks': 'off',
295 | 'padding-line-between-statements': 'error',
296 | 'prefer-arrow-callback': 'off',
297 | 'prefer-const': 'off',
298 | 'prefer-destructuring': 'off',
299 | 'prefer-exponentiation-operator': 'error',
300 | 'prefer-named-capture-group': 'error',
301 | 'prefer-numeric-literals': 'error',
302 | 'prefer-object-spread': 'off',
303 | 'prefer-promise-reject-errors': 'error',
304 | 'prefer-reflect': 'off',
305 | 'prefer-regex-literals': 'error',
306 | 'prefer-rest-params': 'off',
307 | 'prefer-spread': 'off',
308 | 'prefer-template': 'off',
309 | 'quote-props': 'off',
310 | radix: 'error',
311 | 'require-atomic-updates': 'error',
312 | 'require-await': 'error',
313 | 'require-jsdoc': 'error',
314 | 'require-unicode-regexp': 'off',
315 | 'rest-spread-spacing': 'error',
316 | semi: [
317 | 'warn',
318 | 'never'
319 | ],
320 | 'semi-spacing': 'error',
321 | 'semi-style': 'error',
322 | 'sort-imports': 'error',
323 | 'sort-keys': 'off',
324 | 'sort-vars': 'off',
325 | 'space-before-blocks': 'off',
326 | 'space-before-function-paren': 'off',
327 | 'space-in-parens': 'off',
328 | 'space-infix-ops': 'off',
329 | 'space-unary-ops': 'off',
330 | 'spaced-comment': 'off',
331 | strict: 'error',
332 | 'switch-colon-spacing': [
333 | 'error',
334 | {
335 | after: true,
336 | before: false
337 | }
338 | ],
339 | 'symbol-description': 'error',
340 | 'template-curly-spacing': 0,
341 | 'template-tag-spacing': 'error',
342 | 'unicode-bom': [
343 | 'error',
344 | 'never'
345 | ],
346 | 'valid-jsdoc': 'off',
347 | 'vars-on-top': 'off',
348 | 'wrap-iife': 'error',
349 | 'wrap-regex': 'error',
350 | 'yield-star-spacing': 'error',
351 | yoda: [
352 | 'error',
353 | 'never'
354 | ]
355 | },
356 | }
357 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # VScode
2 | .vscode
3 | .vscode/cSpell.json
4 | .vscode/settings.json
5 |
6 | # Logs
7 | logs
8 | *.log
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # Optional npm cache directory
48 | .npm
49 |
50 | # Optional eslint cache
51 | .eslintcache
52 |
53 | # Optional REPL history
54 | .node_repl_history
55 |
56 | # Output of 'npm pack'
57 | *.tgz
58 |
59 | # Yarn Integrity file
60 | .yarn-integrity
61 |
62 | # dotenv environment variables file
63 | .env
64 |
65 | # next.js build output
66 | .next
67 |
68 | # =========================
69 | # Operating System Files
70 | # =========================
71 |
72 | # OSX
73 | # =========================
74 |
75 | .DS_Store
76 | .AppleDouble
77 | .LSOverride
78 |
79 | # Thumbnails
80 | ._*
81 |
82 | # Files that might appear in the root of a volume
83 | .DocumentRevisions-V100
84 | .fseventsd
85 | .Spotlight-V100
86 | .TemporaryItems
87 | .Trashes
88 | .VolumeIcon.icns
89 |
90 | # Directories potentially created on remote AFP share
91 | .AppleDB
92 | .AppleDesktop
93 | Network Trash Folder
94 | Temporary Items
95 | .apdisk
96 |
97 | # Windows
98 | # =========================
99 |
100 | # Windows image file caches
101 | Thumbs.db
102 | ehthumbs.db
103 |
104 | # Folder config file
105 | Desktop.ini
106 |
107 | # Recycle Bin used on file shares
108 | $RECYCLE.BIN/
109 |
110 | # Windows Installer files
111 | *.cab
112 | *.msi
113 | *.msm
114 | *.msp
115 |
116 | # Windows shortcuts
117 | *.lnk
118 |
119 | # No point in committing these
120 | _node_modules
121 |
122 | # These are private
123 | *.code-workspace
124 |
125 | # We shouldn't commit files with names starting with _
126 | _*
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### 0.3.2 - Minor enhancements
2 |
3 | - Enhance Reporting, improve comments, rename nodeGo() to nodeInstance() for clarity
4 |
5 | ### 0.3.1 - Bug fix & code tidy
6 |
7 | * Properly isolate the JS code in the html file to prevent name clashes
8 | * Change `text/x-red` to `text/html` in html file
9 | * Breaking change: use of ES6 means that minimum Node.js version is now v10
10 |
11 | ### 0.2.0 - Update for Node.JS v8 and Node-RED v0.19
12 |
13 | * Tidied up the code
14 | * Improved code by splitting out complex vars/fns
15 | * Improved JSDoc comments
16 | * Added some links to official documentation
17 | * Improve some of the logic
18 | * Added an example configuration node
19 |
20 | NB: The Editable List node is still not working. Sorry, I'll try to get to that next.
21 |
22 | ### 0.1.1 - Minor tweaks
23 |
24 | ### 0.0.0 - not a release
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 |
3 | Apache License
4 | Version 2.0, January 2004
5 | http://www.apache.org/licenses/
6 |
7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8 |
9 | 1. Definitions.
10 |
11 | "License" shall mean the terms and conditions for use, reproduction,
12 | and distribution as defined by Sections 1 through 9 of this document.
13 |
14 | "Licensor" shall mean the copyright owner or entity authorized by
15 | the copyright owner that is granting the License.
16 |
17 | "Legal Entity" shall mean the union of the acting entity and all
18 | other entities that control, are controlled by, or are under common
19 | control with that entity. For the purposes of this definition,
20 | "control" means (i) the power, direct or indirect, to cause the
21 | direction or management of such entity, whether by contract or
22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
23 | outstanding shares, or (iii) beneficial ownership of such entity.
24 |
25 | "You" (or "Your") shall mean an individual or Legal Entity
26 | exercising permissions granted by this License.
27 |
28 | "Source" form shall mean the preferred form for making modifications,
29 | including but not limited to software source code, documentation
30 | source, and configuration files.
31 |
32 | "Object" form shall mean any form resulting from mechanical
33 | transformation or translation of a Source form, including but
34 | not limited to compiled object code, generated documentation,
35 | and conversions to other media types.
36 |
37 | "Work" shall mean the work of authorship, whether in Source or
38 | Object form, made available under the License, as indicated by a
39 | copyright notice that is included in or attached to the work
40 | (an example is provided in the Appendix below).
41 |
42 | "Derivative Works" shall mean any work, whether in Source or Object
43 | form, that is based on (or derived from) the Work and for which the
44 | editorial revisions, annotations, elaborations, or other modifications
45 | represent, as a whole, an original work of authorship. For the purposes
46 | of this License, Derivative Works shall not include works that remain
47 | separable from, or merely link (or bind by name) to the interfaces of,
48 | the Work and Derivative Works thereof.
49 |
50 | "Contribution" shall mean any work of authorship, including
51 | the original version of the Work and any modifications or additions
52 | to that Work or Derivative Works thereof, that is intentionally
53 | submitted to Licensor for inclusion in the Work by the copyright owner
54 | or by an individual or Legal Entity authorized to submit on behalf of
55 | the copyright owner. For the purposes of this definition, "submitted"
56 | means any form of electronic, verbal, or written communication sent
57 | to the Licensor or its representatives, including but not limited to
58 | communication on electronic mailing lists, source code control systems,
59 | and issue tracking systems that are managed by, or on behalf of, the
60 | Licensor for the purpose of discussing and improving the Work, but
61 | excluding communication that is conspicuously marked or otherwise
62 | designated in writing by the copyright owner as "Not a Contribution."
63 |
64 | "Contributor" shall mean Licensor and any individual or Legal Entity
65 | on behalf of whom a Contribution has been received by Licensor and
66 | subsequently incorporated within the Work.
67 |
68 | 2. Grant of Copyright License. Subject to the terms and conditions of
69 | this License, each Contributor hereby grants to You a perpetual,
70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
71 | copyright license to reproduce, prepare Derivative Works of,
72 | publicly display, publicly perform, sublicense, and distribute the
73 | Work and such Derivative Works in Source or Object form.
74 |
75 | 3. Grant of Patent License. Subject to the terms and conditions of
76 | this License, each Contributor hereby grants to You a perpetual,
77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
78 | (except as stated in this section) patent license to make, have made,
79 | use, offer to sell, sell, import, and otherwise transfer the Work,
80 | where such license applies only to those patent claims licensable
81 | by such Contributor that are necessarily infringed by their
82 | Contribution(s) alone or by combination of their Contribution(s)
83 | with the Work to which such Contribution(s) was submitted. If You
84 | institute patent litigation against any entity (including a
85 | cross-claim or counterclaim in a lawsuit) alleging that the Work
86 | or a Contribution incorporated within the Work constitutes direct
87 | or contributory patent infringement, then any patent licenses
88 | granted to You under this License for that Work shall terminate
89 | as of the date such litigation is filed.
90 |
91 | 4. Redistribution. You may reproduce and distribute copies of the
92 | Work or Derivative Works thereof in any medium, with or without
93 | modifications, and in Source or Object form, provided that You
94 | meet the following conditions:
95 |
96 | (a) You must give any other recipients of the Work or
97 | Derivative Works a copy of this License; and
98 |
99 | (b) You must cause any modified files to carry prominent notices
100 | stating that You changed the files; and
101 |
102 | (c) You must retain, in the Source form of any Derivative Works
103 | that You distribute, all copyright, patent, trademark, and
104 | attribution notices from the Source form of the Work,
105 | excluding those notices that do not pertain to any part of
106 | the Derivative Works; and
107 |
108 | (d) If the Work includes a "NOTICE" text file as part of its
109 | distribution, then any Derivative Works that You distribute must
110 | include a readable copy of the attribution notices contained
111 | within such NOTICE file, excluding those notices that do not
112 | pertain to any part of the Derivative Works, in at least one
113 | of the following places: within a NOTICE text file distributed
114 | as part of the Derivative Works; within the Source form or
115 | documentation, if provided along with the Derivative Works; or,
116 | within a display generated by the Derivative Works, if and
117 | wherever such third-party notices normally appear. The contents
118 | of the NOTICE file are for informational purposes only and
119 | do not modify the License. You may add Your own attribution
120 | notices within Derivative Works that You distribute, alongside
121 | or as an addendum to the NOTICE text from the Work, provided
122 | that such additional attribution notices cannot be construed
123 | as modifying the License.
124 |
125 | You may add Your own copyright statement to Your modifications and
126 | may provide additional or different license terms and conditions
127 | for use, reproduction, or distribution of Your modifications, or
128 | for any such Derivative Works as a whole, provided Your use,
129 | reproduction, and distribution of the Work otherwise complies with
130 | the conditions stated in this License.
131 |
132 | 5. Submission of Contributions. Unless You explicitly state otherwise,
133 | any Contribution intentionally submitted for inclusion in the Work
134 | by You to the Licensor shall be under the terms and conditions of
135 | this License, without any additional terms or conditions.
136 | Notwithstanding the above, nothing herein shall supersede or modify
137 | the terms of any separate license agreement you may have executed
138 | with Licensor regarding such Contributions.
139 |
140 | 6. Trademarks. This License does not grant permission to use the trade
141 | names, trademarks, service marks, or product names of the Licensor,
142 | except as required for reasonable and customary use in describing the
143 | origin of the Work and reproducing the content of the NOTICE file.
144 |
145 | 7. Disclaimer of Warranty. Unless required by applicable law or
146 | agreed to in writing, Licensor provides the Work (and each
147 | Contributor provides its Contributions) on an "AS IS" BASIS,
148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
149 | implied, including, without limitation, any warranties or conditions
150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
151 | PARTICULAR PURPOSE. You are solely responsible for determining the
152 | appropriateness of using or redistributing the Work and assume any
153 | risks associated with Your exercise of permissions under this License.
154 |
155 | 8. Limitation of Liability. In no event and under no legal theory,
156 | whether in tort (including negligence), contract, or otherwise,
157 | unless required by applicable law (such as deliberate and grossly
158 | negligent acts) or agreed to in writing, shall any Contributor be
159 | liable to You for damages, including any direct, indirect, special,
160 | incidental, or consequential damages of any character arising as a
161 | result of this License or out of the use or inability to use the
162 | Work (including but not limited to damages for loss of goodwill,
163 | work stoppage, computer failure or malfunction, or any and all
164 | other commercial damages or losses), even if such Contributor
165 | has been advised of the possibility of such damages.
166 |
167 | 9. Accepting Warranty or Additional Liability. While redistributing
168 | the Work or Derivative Works thereof, You may choose to offer,
169 | and charge a fee for, acceptance of support, warranty, indemnity,
170 | or other liability obligations and/or rights consistent with this
171 | License. However, in accepting such obligations, You may act only
172 | on Your own behalf and on Your sole responsibility, not on behalf
173 | of any other Contributor, and only if You agree to indemnify,
174 | defend, and hold each Contributor harmless for any liability
175 | incurred by, or claims asserted against, such Contributor by reason
176 | of your accepting any such warranty or additional liability.
177 |
178 | END OF TERMS AND CONDITIONS
179 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # node-red-contrib-jktesting
2 |
3 | An EXPERIMENTAL Node-RED testing/learning set of nodes.
4 |
5 | Designed as an *experimental* learning and testing tool for trying out different things when designing Node-RED nodes.
6 |
7 | You can also use it as a template for writing your own nodes.
8 |
9 | This node has several purposes:
10 | 1. Learn the order of execution and events for learning how to program you own nodes.
11 | 2. Show some best practices and standards for code when developing your own nodes.
12 | 3. Act as a template I or you can use for your own nodes.
13 |
14 | ## Node Creation Workflow
15 |
16 | The workflow I use to create new nodes is:
17 |
18 | 1. Create a new repo on GitHub - don't yet tag it with node-red until you are ready to go public
19 | 2. Clone the repo to my source files folder on the dev machine
20 | 3. Manually create a soft-link from the new folder into ~/.node-red/node_modules (I don't use npm link as that does strange things like creating a global link)
21 | 4. Create the package.json, README.md, LICENCE, CHANGELOG.md, .gitignore files in my source folder. Create a sub-folder called "nodes" and create the html and js files in there.
22 | 5. In the source folder, do npm install if you need to install any dependencies
23 | 6. Restart Node-RED.
24 |
25 | ## Design
26 |
27 | ### Physical file/folder location summary
28 |
29 | Folders and files for resources on the device running Node-RED are:
30 |
31 | -
32 |
33 | ## Known Issues
34 |
35 | -
36 |
37 | ## To Do
38 |
39 | -
40 |
41 | ## Changes
42 |
43 | See the [Change Log](CHANGELOG) for details.
44 |
45 |
46 | ## Pre-requisites
47 |
48 | See the package.json file for details.
49 |
50 | Requires at least Node.JS v6 and Node-RED v0.16
51 |
52 | ## Install
53 |
54 | Run the following command in your Node-RED user directory (typically `~/.node-red`):
55 |
56 | ```
57 | npm install node-red-contrib-jktesting
58 | ```
59 |
60 | Run Node-RED and add an instance of the test node.
61 |
62 | There is also a simple example in the library.
63 |
64 | ## Nodes
65 |
66 | ### JK Simple Test (jktest)
67 |
68 | #### Node Instance Settings
69 |
70 | Each instance of the test node has the following settings available.
71 |
72 |
73 | - Name
74 | - A short description shown in the admin interface
75 | - Topic
76 | - A topic name to use if the incoming msg does not contain one.
77 | - Server
78 | - An example of using a configuration node.
79 |
80 |
81 | #### Inputs
82 |
83 | - payload `string | buffer`
84 | - The payload of the message to publish.
85 | - topic (optional) `string`
86 | - The MQTT topic to publish to.
87 |
88 |
89 | #### Outputs
90 | - Standard output
91 |
92 |
93 | - _msgcounter integer
94 | - The number of messages recieved by the node instance since either the last reset of Node-RED or the last deployment of the node instance.
95 | - payload string | buffer
96 | - A copy of any inbound payload.
97 | - topic string
98 | - A copy of any inbound topic if present. Otherwise, the topic from the node's settings.
99 | - config object
100 | - A copy of the name and server from the configuration.
101 |
102 |
103 | ## Discussions and suggestions
104 |
105 | Use the [Node-RED google group](https://groups.google.com/forum/#!forum/node-red) for general discussion about this node. Or use the
106 | [GitHub issues log](https://github.com/TotallyInformation/node-red-contrib-jktesting/issues) for raising issues or contributing suggestions and enhancements.
107 |
108 | ## Contributing
109 |
110 | If you would like to contribute to this node, you can contact Totally Information via GitHub or raise a request in the GitHub issues log.
111 |
112 | ## Developers/Contributors
113 |
114 | - [Julian Knight](https://github.com/TotallyInformation)
115 |
--------------------------------------------------------------------------------
/examples/simple-test.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "8e83faec.dc03f8",
4 | "type": "inject",
5 | "z": "595546f3.5fb258",
6 | "name": "",
7 | "topic": "",
8 | "payload": "",
9 | "payloadType": "date",
10 | "repeat": "",
11 | "crontab": "",
12 | "once": false,
13 | "onceDelay": 0.1,
14 | "x": 140,
15 | "y": 180,
16 | "wires": [
17 | [
18 | "a9fadf76.9c6fb"
19 | ]
20 | ]
21 | },
22 | {
23 | "id": "9c5a199f.631d98",
24 | "type": "debug",
25 | "z": "595546f3.5fb258",
26 | "name": "",
27 | "active": true,
28 | "tosidebar": true,
29 | "console": false,
30 | "tostatus": false,
31 | "complete": "true",
32 | "x": 560,
33 | "y": 180,
34 | "wires": []
35 | },
36 | {
37 | "id": "a9fadf76.9c6fb",
38 | "type": "jktest",
39 | "z": "595546f3.5fb258",
40 | "name": "Test Node",
41 | "topic": "",
42 | "server": "ed33c58d.140968",
43 | "x": 300,
44 | "y": 180,
45 | "wires": [
46 | [
47 | "9c5a199f.631d98"
48 | ]
49 | ]
50 | },
51 | {
52 | "id": "ed33c58d.140968",
53 | "type": "jktest-server",
54 | "z": "",
55 | "protocol": "https",
56 | "host": "localhost",
57 | "port": "9090"
58 | }
59 | ]
60 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable sonarjs/no-duplicate-string */
2 | /* eslint-disable jsdoc/newline-after-description, jsdoc/require-param */
3 |
4 | /**
5 | * https://semaphoreci.com/community/tutorials/getting-started-with-gulp-js
6 | * https://gulpjs.com/plugins/
7 | * https://gulpjs.com/docs/en/api/concepts/
8 | * Plugins
9 | * https://www.npmjs.com/package/gulp-include - source file inline replacements
10 | * https://www.npmjs.com/package/gulp-uglify - Minify
11 | * https://www.npmjs.com/package/gulp-rename - Rename source filename on output
12 | * https://www.npmjs.com/package/gulp-once - Only do things if files have changed
13 | * https://www.npmjs.com/package/gulp-replace - String replacer
14 | * https://www.npmjs.com/package/gulp-debug
15 | *
16 | * https://www.npmjs.com/package/gulp-concat
17 | * https://www.npmjs.com/package/gulp-sourcemaps
18 | * https://www.npmjs.com/package/gulp-prompt - get input from user
19 | * https://www.npmjs.com/package/gulp-if-else
20 | * https://www.npmjs.com/package/gulp-minify-inline
21 | * https://www.npmjs.com/package/gulp-tap - Easily tap into a pipeline. Could replace gulp-replace
22 | * https://www.npmjs.com/package/webpack-stream - Use WebPack with gulp
23 | * https://www.npmjs.com/package/tinyify - runs various optimizations
24 | *
25 | * ❌https://www.npmjs.com/package/gulp-changed - Does not work as expected
26 | */
27 |
28 | 'use strict'
29 |
30 | //const { src, dest, series, watch, /* parallel, */ } = require('gulp')
31 | // const uglify = require('gulp-uglify')
32 | // const rename = require('gulp-rename')
33 | // const include = require('gulp-include')
34 | // const once = require('gulp-once')
35 | //const prompt = require('gulp-prompt')
36 | // const replace = require('gulp-replace')
37 | // const debug = require('gulp-debug')
38 | const execa = require('execa')
39 | const fs = require('fs')
40 | //const { promisify } = require('util')
41 | //const dotenv = require('dotenv')
42 |
43 | // const nodeDest = 'nodes'
44 |
45 | // print output of commands into the terminal
46 | const stdio = 'inherit'
47 |
48 | const { version } = JSON.parse(fs.readFileSync('package.json'))
49 |
50 | // What release/version do we want to end up with?
51 | const release = '0.4.0'
52 |
53 | console.log(`Current Version: ${version}. Requested Version: ${release}`)
54 |
55 | /**
56 | * TODO
57 | * - Add text replace to ensure 2021 in (c) blocks is current year
58 | */
59 |
60 | /** Combine the parts of uibuilder.html */
61 | // function combineHtml(cb) {
62 | // src('src/editor/main.html')
63 | // .pipe(include())
64 | // .pipe(once())
65 | // .pipe(rename('uibuilder.html'))
66 | // .pipe(dest(nodeDest))
67 |
68 | // cb()
69 | // }
70 |
71 | /** Watch for changes during development of uibuilderfe & editor */
72 | // function watchme(cb) {
73 | // // Re-combine uibuilder.html if the source changes
74 | // watch('src/editor/*', combineHtml)
75 |
76 | // cb()
77 | // }
78 |
79 | /** Set uibuilder version in package.json */
80 | async function setPackageVersion() {
81 | if (version !== release) {
82 | // bump version without committing and tagging: npm version 4.2.1 --no-git-tag-version --allow-same-version
83 | await execa('npm', ['version', release, '--no-git-tag-version'], {stdio})
84 | } else {
85 | console.log('Requested version is same as current version - nothing will change')
86 | }
87 | }
88 |
89 | /** Create a new GitHub tag for a release (only if release ver # different to last committed tag) */
90 | async function createTag(cb) {
91 | //Get the last committed tag: git describe --tags --abbrev=0
92 | const lastTag = (await execa('git', ['describe', '--tags', '--abbrev=0'])).stdout
93 | console.log(`Last committed tag: ${lastTag}`)
94 |
95 | // If the last committed tag is different to the required release ...
96 | if ( lastTag.replace('v','') !== release ) {
97 | //const commitMsg = `chore: release ${release}`
98 | //await execa('git', ['add', '.'], { stdio })
99 | //await execa('git', ['commit', '--message', commitMsg], { stdio })
100 | await execa('git', ['tag', `v${release}`], { stdio })
101 | await execa('git', ['push', '--follow-tags'], { stdio })
102 | await execa('git', ['push', 'origin', '--tags'], { stdio })
103 | } else {
104 | console.log('Requested release version is same as the latest tag - not creating tag')
105 | }
106 | cb()
107 | }
108 |
109 |
110 | //exports.default = series( packfe, combineHtml ) // series(runLinter,parallel(generateCSS,generateHTML),runTests)
111 | // exports.watch = watchme
112 | // exports.combineHtml = combineHtml
113 | exports.createTag = createTag
114 | exports.setVersion = setPackageVersion //series( setPackageVersion, setFeVersion, setFeVersionMin )
115 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | /** standard object definitions
2 | * Used by code editors & typescript to validate data.
3 | */
4 |
5 | ///
6 | ///
7 | ///
8 |
9 | import { Node } from 'node-red';
10 |
11 | declare global {
12 | var RED:any;
13 | }
14 |
15 | /**
16 | * @typedef {object} myNode Local copy of the node instance config + other info
17 | * @property {String} id Unique identifier for this instance
18 | * @property {String} type What type of node is this an instance of? (uibuilder)
19 | * --- Node's config properties
20 | * @property {String} name Descriptive name, only used by Editor
21 | * @property {String} topic msg.topic overrides incoming msg.topic
22 | * --- Node functions
23 | * @property {Function} send Send a Node-RED msg to an output port
24 | * @property {Function=} done Dummy done function for pre-Node-RED 1.0 servers
25 | * @property {Function=} on Event handler
26 | * @property {Function=} removeListener Event handling
27 | * z, wires
28 | */
29 | export interface myNode extends Node {}
30 |
31 |
32 |
--------------------------------------------------------------------------------
/nodes/jktest-server.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
61 |
62 |
81 |
82 |
99 |
--------------------------------------------------------------------------------
/nodes/jktest-server.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2020 Julian Knight (Totally Information)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an 'AS IS' BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | 'use strict'
18 |
19 | /** ==== THIS IS AN EXAMPLE CONFIGURATION NODE ====
20 | * It doesn't actually do anything, it is just an example
21 | * @see https://nodered.org/docs/creating-nodes/config-nodes
22 | **/
23 |
24 | // Node name must match this nodes html file name AND the nodeType in the html file
25 | const nodeName = 'jktest-server'
26 |
27 | // THIS FUNCTION IS EXECUTED ONLY ONCE AS NODE-RED IS LOADING
28 | module.exports = function(RED) {
29 | 'use strict'
30 |
31 | /** RED, parent object set by Node-RED
32 | * @external RED
33 | * @see https://nodered.org/docs/creating-nodes/node-js
34 | **/
35 |
36 | /** The node's instance definition.
37 | * THIS FUNCTION IS RUN ON (RE)DEPLOYMENT - FOR EACH INSTANCE OF THIS NODE TYPE
38 | * -------------- --------
39 | * this/node var is rebuilt on every redeployment
40 | * @param {object} config - The config vars defined in the matching html file used in the admin interface
41 | */
42 | function nodeGo(config) {
43 |
44 | // Create the node instance
45 | RED.nodes.createNode(this, config)
46 |
47 | // copy 'this' object in case we need it in context of callbacks of other functions.
48 | const node = this
49 |
50 | /** Create local copies of the node configuration (as defined in the .html file)
51 | * NB: Best to use defaults here as well as in the html file for safety
52 | **/
53 | node.protocol = config.protocol || 'http'
54 | node.host = config.host || ''
55 | node.port = config.port || ''
56 |
57 | // See jktest.js for additional processing code if required
58 | // This simple example just stores the settings above
59 | // they might be shared by all instances of the parent node.
60 |
61 | } // ---- End of nodeGo (initialised node instance) ---- //
62 |
63 | /** Register the node by name. This must be called before overriding any of the node functions.
64 | * @param {string} nodeName - Name used in the matching html file that defines the admin ui
65 | **/
66 | RED.nodes.registerType(nodeName, nodeGo)
67 |
68 | } // ---- End of module.exports ---- //
69 |
70 | // EOF
71 |
--------------------------------------------------------------------------------
/nodes/jktest.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
147 |
148 |
201 |
202 |
269 |
--------------------------------------------------------------------------------
/nodes/jktest.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2020 Julian Knight (Totally Information)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an 'AS IS' BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | 'use strict'
18 |
19 | /** The HTML file defines the settings and help information for this node type
20 | * The JS file defines the processing for each instance of this node type
21 | **/
22 |
23 | const nodeVersion = require('../package.json').version
24 |
25 | // THIS OUTER SECTION IS EXECUTED ONLY ONCE AS NODE-RED IS LOADING
26 | console.log(`[jktesting] *** jktest.js loaded into memory and executed. Module Ver: ${nodeVersion} ***`)
27 |
28 | // Node name must match this nodes html file name AND the nodeType in the html file
29 | const nodeName = 'jktest'
30 |
31 | // Set to false to prevent debugging output
32 | var debug = true
33 |
34 | //debug && console.log( 'node-red-contrib-' + nodeName + ' - initialising module. Module Version: ' + nodeVersion )
35 |
36 | // Keep track of how many times each instance of this node is deployed
37 | const deployments = {}
38 |
39 | //const events = require('events')
40 | //var ev = new events.EventEmitter();
41 | //ev.setMaxListeners(0);
42 |
43 | // THIS FUNCTION IS EXECUTED ONLY ONCE AS NODE-RED IS LOADING
44 | module.exports = function(RED) {
45 | 'use strict'
46 |
47 | /** Some useful properties of the RED object
48 | * RED.events
49 | * See below
50 | * 'registry:plugin-added', 'event-log'
51 | * RED.settings
52 | * NB: entries in settings.js are read-only and shouldn't be read using RED.settings.get, that is only for settings that can change in-flight.
53 | * see Node-RED issue #1543.
54 | * RED.httpNode || RED.httpAdmin
55 | * References to the ExpressJS app service for user-facing || Editor/admin-facing servers
56 | * RED.nodes
57 | * createNode(), getNode(), eachNode(), addCredentials(), getCredentials(), deleteCredentials(), registerType(), registerSubflow()
58 | * RED.comms
59 | * RED.comms.publish (topic, data, retain)
60 | * Consume in Editor as
61 | * RED.comms.subscribe("status/#",function(topic,msg) {...})
62 | * RED.hooks - https://github.com/node-red/designs/tree/master/designs/pluggable-message-routing
63 | * 'has', 'clear', 'add', 'remove', 'trigger'
64 | * RED.util
65 | * 'encodeObject', 'ensureString', 'ensureBuffer', 'cloneMessage', 'compareObjects',
66 | * 'generateId', 'getMessageProperty', 'setMessageProperty', 'getObjectProperty', 'setObjectProperty',
67 | * 'evaluateNodeProperty', 'normalisePropertyExpression', 'normaliseNodeTypeName', 'prepareJSONataExpression',
68 | * 'evaluateJSONataExpression', 'parseContextStore'
69 | *
70 | * Other props
71 | * 'log', 'util', 'version', 'require', 'plugins', 'library', 'httpNode', 'httpAdmin', 'server', 'auth', '_'
72 | */
73 |
74 | //debug && RED.log.debug( 'node-red-contrib-' + nodeName + ' - loading module' )
75 | console.log('[jktesting] *** jktest.js module.exports function executed (RED object now available) ***')
76 | //console.log(RED.nodes)
77 |
78 | /** Node-RED Events
79 | RED.events.on('flows:started',function() {
80 | console.log('[jktesting:jktest.js] ****** All nodes have started ******')
81 | })
82 | RED.events.on('flows:stopped',function() {
83 | console.log('[jktesting:jktest.js] ****** All nodes have stopped ******')
84 | })
85 | */
86 | /** Show when Node-RED runtime events that relate to this node happen */
87 | RED.events.on('runtime-event', function(event) {
88 | console.log('[jktesting] jktest.js runtime-event: ', event)
89 | if (event.id === 'project-update' && event.payload.action === 'loaded') {
90 | console.log('A new project has been loaded')
91 | }
92 | })
93 |
94 | /** Settings that can be set in settings.js and that are shared with the node editor
95 | * Passed to the `RED.nodes.registerType` function
96 | * @see https://nodered.org/docs/creating-nodes/node-js#custom-node-settings
97 | * Also must contain any credentials used in the admin ui in a ... credentials: {} ... object
98 | * @see https://nodered.org/docs/creating-nodes/credentials
99 | **/
100 | const nodeSettings = {
101 | 'settings': {
102 | 'jktestNodeEnv': { 'value': process.env.NODE_ENV, 'exportable': true }
103 | },
104 | // 'credentials': {
105 | // id: {type:text},
106 | // pw: {type:password},
107 | // }
108 | }
109 |
110 | /** RED, parent object set by Node-RED
111 | * @external RED
112 | * @see https://nodered.org/docs/creating-nodes/node-js
113 | **/
114 |
115 | /** The node's instance definition.
116 | * THIS FUNCTION IS RUN ON (RE)DEPLOYMENT - FOR EACH INSTANCE OF THIS NODE TYPE
117 | * -------------- --------
118 | *
119 | * this/node var is rebuilt on every redeployment
120 | *
121 | * @param {object} config - The config vars defined in the matching html file used in the admin interface
122 | *
123 | * node.xxx != var xxx - though the way that NR processes this fn makes them very similar in this case.
124 | * node.xxx vars would be accessible wherever the node object is referenced.
125 | * var xxx vars are only accessible inside this function.
126 | */
127 | function nodeInstance(config) {
128 |
129 | //debug && RED.log.debug( 'node-red-contrib-' + nodeName + ' - Starting nodeInstance, node instance being deployed ' )
130 |
131 | /** Create the node instance
132 | * @param {Object} this A node instance object of type `nodeInstance` in this case
133 | * @param {Object} config Configuration data passed from the Editor @see https://nodered.org/docs/creating-nodes/properties
134 | */
135 | RED.nodes.createNode(this, config)
136 |
137 | // copy 'this' object in case we need it in context of callbacks of other functions.
138 | const node = this
139 |
140 | console.log(`[jktesting] *** jktest.js/nodeInstance() - Creating instance of jktest node - ID: ${this.id} ***`) //, config, node)
141 |
142 | /** Create local copies of the node configuration (as defined in the .html file)
143 | * NB: Best to use defaults here as well as in the html file for safety
144 | **/
145 | node.name = config.name || ''
146 | node.topic = config.topic || '' // NB: will be overwritten by msg.topic if received
147 |
148 | // example configuration node - config node with 3 fields
149 | let configObj = RED.nodes.getNode(config.server)
150 | node.server = configObj.protocol + '://' + configObj.host
151 | if ( configObj.port !== '' ) node.server += ':' + configObj.port
152 |
153 | /** Built-in attributes:
154 | * node.id // the node instance unique id, also available as config.id
155 | * node.type // the module name
156 | **/
157 |
158 | /** Use this if you want to see what keys (properties) are available in either the config or node objects
159 | * console.dir(Object.keys(config))
160 | * console.dir(Object.keys(node))
161 | **/
162 |
163 | /** Access context/flow/global variables
164 | * NB: context vars are reset on node, flow and full redeployments - eventually, context vars may get a persistence mechanism
165 | * node.context().get('varname')
166 | * node.context().set('varname', someContent)
167 | * NB: flow and global vars are NOT reset on any kind of redeployment
168 | * node.context().flow.get('varname')
169 | * node.context().global.get('varname')
170 | **/
171 |
172 | // Track how many messages recieved (using ES6 generator function) - not essential but nice for tracking
173 | node.msgCounter = rcvCounter()
174 |
175 | // Set to true if you want additional debug output to the console
176 | debug = RED.settings.debug || true
177 |
178 | // Keep track of the number of times each instance is deployed.
179 | // The initial deployment = 1
180 | if ( deployments.hasOwnProperty(node.id) ) {
181 | deployments[node.id]++
182 | } else {
183 | deployments[node.id] = 1
184 | }
185 |
186 | debug && RED.log.debug(
187 | 'node-red-contrib-' + nodeName + ', # Deployments: ' + deployments[node.id] +
188 | ', node.ID: ' + node.id + ', node.type: ' + node.type +
189 | ', Instance Name: ' + node.name)
190 |
191 | /** If we need an Express app server to serve a web page
192 | * const app = RED.httpNode || RED.httpAdmin
193 | * If we just need the http server
194 | * const httpServer = RED.server
195 | **/
196 |
197 | /** These ONLY log to the NR console (audit is only shown when requested in NR settings)
198 | * RED.log.info('Some Text') // also info, log, warn, error, trace, debug
199 | * RED.log.audit({ 'TEST': 'An Object' })
200 | * These show up in the NR console AND in the NR debug window
201 | * this.log("Something happened");
202 | * this.warn("Something happened you should know about");
203 | * this.error("Oh no, something bad happened");
204 | * this.error("Oh no, something bad happened", msg); // halts current flow, triggers catch node
205 | **/
206 |
207 | // Set a status line under the node instance in the admin interface
208 | setNodeStatus( { fill: 'blue', shape: 'dot', text: 'Node Initialised' }, node )
209 |
210 | /** Handler function for node input events (when a node instance receives a msg)
211 | * @param {Object} msg - The input msg object
212 | **/
213 | function nodeInputHandler(msg) {
214 | debug && RED.log.debug('TEST:nodeInstance:nodeInputHandler') //debug
215 |
216 | // If msg is null, nothing will be sent
217 | if ( msg === null ) return
218 |
219 | // if msg isn't an object
220 | // NOTE: This is paranoid and shouldn't be possible!
221 | if ( typeof msg !== 'object' ) {
222 | // Force msg to be an object with payload of original msg
223 | msg = { 'payload': msg }
224 | }
225 |
226 | // Add topic from node config if present and not present in msg
227 | if ( !(msg.hasOwnProperty('topic')) || msg.topic === '' ) {
228 | if ( node.topic !== '' ) msg.topic = node.topic
229 | else msg.topic = 'TEST/' + nodeName
230 | }
231 |
232 | // Keep this fn small for readability so offload
233 | // any further, more customised code to another fn
234 | inputHandler(msg, node, RED)
235 |
236 | } // -- end of msg received processing -- //
237 |
238 | // Whenever the node instance receives a msg, the function is triggered
239 | node.on('input', nodeInputHandler)
240 |
241 | /** Do something when Node-RED is closing down
242 | * which includes when this node instance is redeployed
243 | * NOTE: function(done) MUST be used if needing to do async processing
244 | * in close, BUT if used, done() MUST be called because Node-RED
245 | * will wait otherwise and timeout with an error after 15 sec.
246 | **/
247 | node.on('close', function(removed, done) {
248 | debug && RED.log.debug('TEST:nodeInstance:on-close') //debug
249 |
250 | // Tidy up the event listener (that listens for new msg's)
251 | node.removeListener('input', nodeInputHandler)
252 |
253 | // Do any complex close processing here if needed - MUST BE LAST
254 | // the function MUST also process done()
255 | processClose(removed, done, node, RED)
256 |
257 | }) // --- End of on close --- //
258 |
259 | } // ---- End of nodeInstance (initialised node instance) ---- //
260 |
261 | /** Register the node by name. This must be called before overriding any of the node functions.
262 | * @param {string} nodeName - Name used in the matching html file that defines the admin ui
263 | * @param {function} nodeInstance - Name of the function that provides the processing for each instance of this node type
264 | * @param {Object=} nodeSettings - An optional object defining settings that can be set in Node-RED's settings.js file. @see https://nodered.org/docs/creating-nodes/node-js#exposing-settings-to-the-editor
265 | **/
266 | RED.nodes.registerType(nodeName, nodeInstance, nodeSettings)
267 | //RED.nodes.registerType(nodeName, nodeInstance, { credentials: { username: {type:"text"}, password: {type:"password"} } })
268 |
269 | } // ---- End of module.exports ---- //
270 |
271 | /** ========== UTILITY FUNCTIONS ==============
272 | * Don't forget to pass msg, node, RED, etc.
273 | * as arguments because these functions are
274 | * outside the scope of the exports function
275 | * =========================================== */
276 |
277 | /** Complex, custom code when processing an incoming msg should go here
278 | * Needs to output the msg object (if needed) before ending.
279 | * - use RED.util.getMessageProperty(msg,expr) to get any element of the msg
280 | * as this lets you retrieve deep info such as msg.payload.sub.deep.
281 | * - use RED.util.setMessageProperty(msg,prop,value,createMissing) to set an element on the msg,
282 | * it will fill in any missing intermediate properties.
283 | * - use RED.comms.publish('A message to admin') to send a message to the admin interface,
284 | * appears as a pop-over message box at the top of the screen.
285 | * @param {Object} msg - The incoming msg object
286 | * @param {Object} node - The node definition object
287 | * @param {Object} RED - The RED object from Node-RED
288 | **/
289 | function inputHandler(msg, node, RED) {
290 | var msgCount = node.msgCounter.next().value
291 | RED.util.setMessageProperty(msg, '_msgcounter', msgCount, false) // iterate counter and attach to msg
292 | setNodeStatus({fill: 'yellow', shape: 'dot', text: 'Message Recieved #' + msgCount}, node)
293 |
294 | //debug && console.dir(msg) //debug
295 |
296 | // Simple example adding the node configuration data to the output message
297 | msg.config = {
298 | 'name': node.name,
299 | 'server': node.server
300 | }
301 |
302 | // Send on the input msg to output
303 | node.send(msg)
304 |
305 | } // ---- End of inputHandler function ---- //
306 |
307 | /** Do any complex, custom node closure code here
308 | * e.g. remove websocket connections
309 | * @param {boolean=} removed - True if the node instance has been removed (@since v0.17)
310 | * @param {any=} done - An internal function if needing async processing or null otherwise
311 | * @param {Object} node - The node definition object
312 | * @param {Object} RED - The RED object from Node-RED
313 | **/
314 | function processClose(removed = null, done = null, node, RED) {
315 | setNodeStatus({fill: 'red', shape: 'ring', text: 'CLOSED'}, node)
316 |
317 | if ( removed ) {
318 | debug && RED.log.debug('TEST:processClose:node instance removed') //debug
319 | } else {
320 | debug && RED.log.debug('TEST:processClose:node instance restarted/deployed') //debug
321 | }
322 |
323 | // This should be executed last if present. `done` is the data returned from the 'close'
324 | // event and is used to resolve async callbacks to allow Node-RED to close
325 | if (done) done()
326 | } // ---- End of processClose function ---- //
327 |
328 | /** Simple fn to set a node status in the admin interface
329 | * @param {string|Object} status - Status object. If a string, will be turned into a default status object
330 | * @param {Object} node - The node definition object
331 | * fill: red, green, yellow, blue or grey
332 | * shape: ring or dot
333 | **/
334 | function setNodeStatus( status, node ) {
335 | if ( typeof status !== 'object' ) status = {fill: 'grey', shape: 'ring', text: status}
336 |
337 | node.status(status)
338 | }
339 |
340 | /** Use an ES6 generator function to track how many messages have been received since the last
341 | * restart of NR or redeploy of this node instance.
342 | * This is obviously TOTALLY OVERKILL since a simple variable would have done just as well but hey, gotta start somewhere, right? ;-)
343 | **/
344 | function* rcvCounter() {
345 | var msgCounter = 1 // start counting from 1
346 | while(true) yield msgCounter++ // keeps going forever!
347 | }
348 |
349 | /** Join any number of string arguments into a valid URL format
350 | * @see http://stackoverflow.com/a/28592528/3016654
351 | * @param {Array.string} urls - a list of strings to join
352 | **/
353 | function urlJoin() {
354 | const trimRegex = new RegExp('^\\/|\\/$','g')
355 | var paths = Array.prototype.slice.call(arguments)
356 | return '/'+paths.map(function(e){return e.replace(trimRegex,'')}).filter(function(e){return e}).join('/')
357 | }
358 |
359 | // EOF
360 |
--------------------------------------------------------------------------------
/nodes/jktest2.html:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
--------------------------------------------------------------------------------
/nodes/jktest2.js:
--------------------------------------------------------------------------------
1 | /** A simple template for defining custom nodes
2 | * Destructured to make for easier and more consistent logic.
3 | */
4 | 'use strict'
5 |
6 | /** --- Type Defs ---
7 | * @typedef {import('../typedefs.js').runtimeRED} runtimeRED
8 | * @typedef {import('../typedefs.js').runtimeNodeConfig} runtimeNodeConfig
9 | * @typedef {import('../typedefs.js').runtimeNode} runtimeNode
10 | * typedef {import('../typedefs.js').myNode} myNode
11 | */
12 |
13 | //#region ----- Module level variables ---- //
14 |
15 | /** Main (module) variables - acts as a configuration object
16 | * that can easily be passed around.
17 | */
18 | const mod = {
19 | /** @type {runtimeRED} Reference to the master RED instance */
20 | RED: undefined,
21 | /** @type {string} Custom Node Name - has to match with html file and package.json `red` section */
22 | nodeName: 'jktest2',
23 |
24 | // Add anything else here that you may wish
25 | // to access from any function.
26 | // Having one object also makes it much easier
27 | // to pass this to external modules as needed.
28 | }
29 |
30 | //#endregion ----- Module level variables ---- //
31 |
32 | //#region ----- Module-level support functions ----- //
33 |
34 | /** 1a) Runs once when Node-RED (re)starts or when the node package is first added */
35 | function moduleSetup() {
36 | // As a module-level named function, it will inherit `mod` and other module-level variables
37 | //const RED = mod.RED
38 |
39 | console.log('>>>=[1a]=>>> [moduleSetup] Startng')
40 |
41 | // Do stuff here that only needs doing once
42 | // Don't forget to push anything that might be needed by other functions and modules
43 | // into the `mod` variable so that it is easily accessible and can be passed on.
44 | }
45 |
46 | /** 3) Run whenever a node instance receives a new input msg
47 | * NOTE: `this` context is still the parent (nodeInstance).
48 | * See https://nodered.org/blog/2019/09/20/node-done
49 | * @param {object} msg The msg object received.
50 | * @param {Function} send Per msg send function, node-red v1+
51 | * @param {Function} done Per msg finish function, node-red v1+
52 | */
53 | function inputMsgHandler(msg, send, done) {
54 | // As a module-level named function, it will inherit `mod` and other module-level variables
55 |
56 | // If you need it - or just use mod.RED if you prefer:
57 | //const RED = mod.RED
58 |
59 | console.log('>>>=[3]=>>> [inputMsgHandler] Startng', msg) //, this)
60 | send(msg)
61 | done()
62 | }
63 |
64 | /** 2) This is run when an actual instance of our node is committed to a flow
65 | * @param {runtimeNodeConfig} config The Node-RED node instance config object
66 | */
67 | function nodeInstance(config) {
68 | // As a module-level named function, it will inherit `mod` and other module-level variables
69 |
70 | // If you need it - which you will here - or just use mod.RED if you prefer:
71 | const RED = mod.RED
72 |
73 | console.log('>>>=[2]=>>> [nodeInstance] Startng')
74 | //console.log('>>>=[2a]=>>>', config)
75 |
76 | // Create the node instance - `this` can only be referenced AFTER here
77 | RED.nodes.createNode(this, config)
78 |
79 | // Transfer config items from the Editor panel to the runtime
80 | this.name = config.name
81 | this.topic = config.topic
82 |
83 | /** Handle incoming msg's - note that the handler fn inherits `this`
84 | * The inputMsgHandler function is executed every time this instance
85 | * of the node receives a msg in a flow.
86 | */
87 | this.on('input', inputMsgHandler)
88 |
89 |
90 | /** Put things here if you need to do anything when a node instance is removed
91 | * Or if Node-RED is shutting down.
92 | * Note the use of an arrow function, ensures that the function keeps the
93 | * same `this` context and so has access to all of the node instance properties.
94 | */
95 | this.on('close', (removed, done) => {
96 | console.log('>>>=[4]=>>> [nodeInstance:close] Closing. Removed?: ', removed)
97 |
98 |
99 | // Give Node-RED a clue when you have finished (more important if your shutdown
100 | // process includes an async task, make sure done() is executed when the async
101 | // task completes, not when this function ends).
102 | done()
103 | })
104 |
105 | /** Properties of `this`
106 | * Methods: updateWires(wires), context(), on(event,callback), emit(event,...args), removeListener(name,listener), removeAllListeners(name), close(removed)
107 | * send(msg), receive(msg), log(msg), warn(msg), error(logMessage,msg), debug(msg), trace(msg), metric(eventname, msg, metricValue), status(status)
108 | * Other: credentials, id, type, z, wires, x, y
109 | * + any props added manually from config, typically at least name and topic
110 | */
111 | //console.log('>>>=[2b]=>>>', this)
112 | }
113 |
114 | //#endregion ----- Module-level support functions ----- //
115 |
116 | /** 1) Complete module definition for our Node. This is where things actually start.
117 | * @param {runtimeRED} RED The Node-RED runtime object
118 | */
119 | function Test2(RED) {
120 | // As a module-level named function, it will inherit `mod` and other module-level variables
121 |
122 | // Save a reference to the RED runtime for convenience
123 | // This allows you to access it from any other function
124 | // defined above.
125 | mod.RED = RED
126 |
127 | // Add function calls here for setup that only needs doing
128 | // once. Node-RED loads this once no matter how many instances
129 | // of you node you add to flows.
130 | moduleSetup() // (1a)
131 |
132 | // Register a new instance of the specified node type (2)
133 | RED.nodes.registerType(mod.nodeName, nodeInstance)
134 | }
135 |
136 | // Export the module definition (1), this is consumed by Node-RED on startup.
137 | module.exports = Test2
138 |
139 | //EOF
140 |
--------------------------------------------------------------------------------
/nodes/jktestEditableList.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
186 |
187 |
212 |
213 |
260 |
--------------------------------------------------------------------------------
/nodes/jktestEditableList.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2017 Julian Knight (Totally Information)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an 'AS IS' BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | 'use strict'
18 |
19 | // Module name must match this nodes html file
20 | const moduleName = 'jktestEditableList'
21 | const nodeVersion = require('../package.json').version
22 |
23 | var debug = true
24 |
25 | debug && console.log( 'node-red-contrib-' + moduleName + ' - initialising module. Module Version: ' + nodeVersion )
26 |
27 | //const events = require('events')
28 | //var ev = new events.EventEmitter();
29 | //ev.setMaxListeners(0);
30 |
31 | module.exports = function(RED) {
32 | 'use strict'
33 |
34 | debug && console.log( 'node-red-contrib-' + moduleName + ' - loading module' )
35 |
36 | function nodeGo(config) {
37 | // This fn is run when NR starts and when this node instance is
38 | // deployed after changes to settings
39 | debug && console.log( 'node-red-contrib-' + moduleName + ' - Starting nodeGo, node being registered ' )
40 |
41 | // Create the node
42 | RED.nodes.createNode(this, config)
43 |
44 | // copy 'this' object in case we need it in context of callbacks of other functions.
45 | const node = this
46 |
47 | // Create local copies of the node configuration (as defined in the .html file)
48 | node.name = config.name || ''
49 | node.topic = config.topic || '' // NB: will be overwritten by msg.topic if recived
50 |
51 | // Track how many messages recieved (using ES6 generator function)
52 | node.msgCounter = rcvCounter()
53 |
54 | // NOTE that this nodes context variables are available from (node.context()).get('varname')
55 | // The node's flow variables are available from (node.context().flow).get('varname')
56 | // The global variables are available from (node.context().global).get('varname')
57 |
58 | // Set to true if you want additional debug output to the console
59 | debug = RED.settings.debug || true
60 |
61 | // If we need an http server to serve a web page
62 | //const app = RED.httpNode || RED.httpAdmin
63 |
64 | // These ONLY log to the NR console (audit is only shown when requested in NR settings)
65 | // RED.log.info('Some Text')
66 | // RED.log.audit({ 'TEST': 'An Object' })
67 | // These show up in the NR console AND in the NR debug window
68 | //this.log("Something happened");
69 | //this.warn("Something happened you should know about");
70 | //this.error("Oh no, something bad happened");
71 | //this.error("Oh no, something bad happened", msg); // halts current flow, triggers catch node
72 |
73 | setNodeStatus( { fill: 'blue', shape: 'dot', text: 'Node Initialised' }, node )
74 |
75 | // handler function for node input events (when a node instance receives a msg)
76 | function nodeInputHandler(msg) {
77 | debug && RED.log.info('TEST:nodeGo:nodeInputHandler') //debug
78 |
79 | // If msg is null, nothing will be sent
80 | if ( msg !== null ) {
81 | // if msg isn't null and isn't an object
82 | // NOTE: This is paranoid and shouldn't be possible!
83 | if ( typeof msg !== 'object' ) {
84 | // Force msg to be an object with payload of original msg
85 | msg = { 'payload': msg }
86 | }
87 | // Add topic from node config if present and not present in msg
88 | if ( !(msg.hasOwnProperty('topic')) || msg.topic === '' ) {
89 | if ( node.topic !== '' ) msg.topic = node.topic
90 | }
91 | }
92 |
93 | // Keep this fn small for readability so offload
94 | // any further, more customised code to another fn
95 | msg = inputHandler(msg, node, RED)
96 |
97 | // Send on the input msg to output
98 | node.send(msg)
99 |
100 | } // -- end of msg recieved processing -- //
101 | node.on('input', nodeInputHandler)
102 |
103 | // Do something when Node-RED is closing down
104 | // which includes when this node instance is redeployed
105 | // NOTE: function(done) MUST be used if needing to do async processing
106 | // in close, BUT if used, done() MUST be called because Node-RED
107 | // will wait otherwise.
108 | // node.on('close', function(done) {
109 | node.on('close', function() {
110 | debug && RED.log.info('TEST:nodeGo:on-close') //debug
111 |
112 | // Resolve for async callbacks (done must be the sole param in fn define) ...
113 | //done()
114 |
115 | // Tidy up the event listener (that listens for new msg's)
116 | node.removeListener('input', nodeInputHandler)
117 |
118 | // Do any complex close processing here if needed - MUST BE LAST
119 | processClose(null, node, RED) // swap with below if needing async
120 | //processClose(done, node, RED)
121 |
122 | }) // --- End of on close --- //
123 |
124 | } // ---- End of nodeGo (initialised node instance) ---- //
125 |
126 | // Register the node by name. This must be called before overriding any of the node functions.
127 | RED.nodes.registerType(moduleName, nodeGo)
128 | //RED.nodes.registerType(moduleName, nodeGo, { credentials: { username: {type:"text"}, password: {type:"password"} } })
129 | }
130 |
131 | // ========== UTILITY FUNCTIONS ================ //
132 |
133 | // Complex, custom code when processing an incoming msg should go here
134 | // Needs to return the msg object
135 | function inputHandler(msg, node, RED) {
136 | msg._msgcounter = node.msgCounter.next().value // iterate counter and attach to msg
137 | setNodeStatus({fill: 'yellow', shape: 'dot', text: 'Message Recieved #' + msg._msgcounter}, node)
138 |
139 | //debug && console.dir(msg) //debug
140 |
141 | return msg
142 | } // ---- End of inputHandler function ---- //
143 |
144 | // Do any complex, custom node closure code here
145 | function processClose(done = null, node, RED) {
146 | setNodeStatus({fill: 'red', shape: 'ring', text: 'CLOSED'}, node)
147 |
148 | // This should be executed last if present. `done` is the data returned from the 'close'
149 | // event and is used to resolve async callbacks to allow Node-RED to close
150 | if (done) done()
151 | } // ---- End of processClose function ---- //
152 |
153 | // Simple fn to set a node status in the admin interface
154 | // fill: red, green, yellow, blue or grey
155 | // shape: ring or dot
156 | function setNodeStatus( status, node ) {
157 | if ( typeof status !== 'object' ) status = {fill: 'grey', shape: 'ring', text: status}
158 |
159 | node.status(status)
160 | }
161 |
162 | // Use an ES6 generator function to track how many messages have been recieved since the last
163 | // restart of NR or redeploy of this node instance.
164 | // This is obviously TOTALLY OVERKILL since a simple variable would have done just as well but hey, gotta start somewhere, right? ;-)
165 | function* rcvCounter() {
166 | var msgCounter = 1 // start counting from 1
167 | while(true) yield msgCounter++
168 | }
169 |
170 | //from: http://stackoverflow.com/a/28592528/3016654
171 | function urlJoin() {
172 | const trimRegex = new RegExp('^\\/|\\/$','g')
173 | var paths = Array.prototype.slice.call(arguments)
174 | return '/'+paths.map(function(e){return e.replace(trimRegex,'')}).filter(function(e){return e}).join('/')
175 | }
176 |
177 | // EOF
178 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-red-contrib-jktesting",
3 | "version": "0.4.0",
4 | "description": "An experimental node for Node-RED for learning and testing node creation ideas. Can also be used as a template for your own nodes.",
5 | "keywords": [
6 | "node-red",
7 | "testing",
8 | "test",
9 | "learning"
10 | ],
11 | "main": "none",
12 | "scripts": {
13 | "build": "gulp build",
14 | "npmtags": "npm dist-tag ls node-red-contrib-uibuilder",
15 | "npmtagnext": "npm dist-tag add node-red-contrib-uibuilder@$npm_package_version next",
16 | "gittags": "git tag",
17 | "gittag": "git tag -a v%npm_package_version%",
18 | "gitpushtags": "git push origin --tags"
19 | },
20 | "homepage": "https://github.com/TotallyInformation/node-red-contrib-jktesting",
21 | "bugs": "https://github.com/TotallyInformation/node-red-contrib-jktesting/issues",
22 | "author": {
23 | "name": "Julian Knight",
24 | "url": "https://github.com/TotallyInformation"
25 | },
26 | "contributors": [
27 | {
28 | "name": "Julian Knight",
29 | "url": "https://github.com/TotallyInformation"
30 | }
31 | ],
32 | "license": "Apache-2.0",
33 | "repository": {
34 | "type": "git",
35 | "url": "https://github.com/TotallyInformation/node-red-contrib-jktesting.git"
36 | },
37 | "node-red": {
38 | "version": ">=2.0.0",
39 | "nodes": {
40 | "jktest": "nodes/jktest.js",
41 | "jktest2": "nodes/jktest2.js",
42 | "jktestEditableList": "nodes/jktestEditableList.js",
43 | "jktest-server": "nodes/jktest-server.js"
44 | },
45 | "notyetnodes": {}
46 | },
47 | "dependencies": {
48 | },
49 | "devDependencies": {
50 | "@types/express": "^4.17.13",
51 | "@types/jquery": "^3.5.6",
52 | "@types/node": "^14.17.14",
53 | "@types/node-red": "^1.1.1",
54 | "eslint": "^7.32.0",
55 | "eslint-plugin-es": "^4.1.0",
56 | "eslint-plugin-html": "^6.1.2",
57 | "eslint-plugin-jsdoc": "^36.0.8",
58 | "eslint-plugin-promise": "^5.1.0",
59 | "eslint-plugin-sonarjs": "^0.10.0",
60 | "execa": "^5.1.1",
61 | "gulp": "^4.0.2",
62 | "gulp-changed": "^4.0.3",
63 | "gulp-include": "^2.4.1",
64 | "gulp-rename": "^2.0.0",
65 | "gulp-uglify": "^3.0.2"
66 | },
67 | "engines": {
68 | "node": ">=12.0"
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/typedefs.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-irregular-whitespace */
2 | /** Define typedefs for linting and JSDoc/ts checks - does not actually contain live code
3 | *
4 | * Copyright (c) 2017-2021 Julian Knight (Totally Information)
5 | * https://it.knightnet.org.uk, https://github.com/TotallyInformation/node-red-contrib-uibuilder
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the 'License');
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an 'AS IS' BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | */
19 |
20 | 'use strict'
21 |
22 | /** editorRED
23 | * typedef {object} editorRED The Node-RED core object available to a custom node's .html file
24 | *
25 | */
26 |
27 | /** runtimeSettings - See settings.js for static settings.
28 | * @typedef {object} runtimeSettings Static and Dynamic settings for Node-RED runtime
29 | *
30 | * @property {string} uiPort The port used by Node-RED (default=1880)
31 | * @property {string} uiHost The host IP used by Node-RED (default=0.0.0.0)
32 | * @property {string} userDir The userDir folder
33 | * @property {string} httpNodeRoot Optional base URL. All user url's will be under this. Default empty string.
34 | * @property {object} FunctionGlobalContext Add values, Functions, packages to the Global context variable store.
35 | * @property {Function} mqttReconnectTime : [Getter/Setter],
36 | * @property {Function} serialReconnectTime : [Getter/Setter],
37 | * @property {Function} debugMaxLength : [Getter/Setter],
38 | * @property {Function} debugUseColors : [Getter/Setter],
39 | * @property {string} flowFile : [Getter/Setter],
40 | * @property {Function} flowFilePretty : [Getter/Setter],
41 | * @property {string} credentialSecret : [Getter/Setter],
42 | * @property {string} httpAdminRoot : [Getter/Setter],
43 | * @property {string} httpStatic : [Getter/Setter],
44 | * @property {Function} adminAuth : [Getter/Setter],
45 | * @property {Function} httpNodeMiddleware : [Getter/Setter],
46 | * @property {Function} httpAdminMiddleware : [Getter/Setter],
47 | * @property {Function} httpServerOptions : [Getter/Setter],
48 | * @property {Function} webSocketNodeVerifyClient : [Getter/Setter],
49 | * @property {Function} exportGlobalContextKeys : [Getter/Setter],
50 | * @property {Function} contextStorage : [Getter/Setter],
51 | * @property {Function} editorTheme : [Getter/Setter],
52 | * @property {string} settingsFile : [Getter/Setter],
53 | * @property {string} httpRoot : [Getter/Setter],
54 | * @property {Function} disableEditor : [Getter/Setter],
55 | * @property {Function} httpAdminAuth : [Getter/Setter],
56 | * @property {Function} httpNodeAuth : [Getter/Setter],
57 | * @property {object|Function} [https] If present, https will be used for ExpressJS servers.
58 | * @property {object} [uibuilder] Optional uibuilder specific Node-RED settings
59 | * @property {number} [uibuilder.port] Port number if uib is using its own ExpressJS instance
60 | * @property {string} [uibuilder.uibRoot] Folder name that will hold all uib runtime and instance folders
61 | *
62 | * @property {string} coreNodesDir Folder containing Node-RED core nodes
63 | * @property {string} version Node-RED version
64 | *
65 | * @property {object} logging Controls the type and amount of logging output
66 | * @property {object} logging.console Controls output levels and types to the console log
67 | * @property {string} logging.console.level What level of output? (fatal, error, warn, info, debug, trace)
68 | * @property {boolean} logging.console.metrics Should metrics also be shown?
69 | * @property {boolean} logging.console.audit Should audit also be shown?
70 | *
71 | * @property {Function} get Get dynamic settings. NB: entries in settings.js are read-only and shouldn't be read using RED.settings.get, that is only for settings that can change in-flight.
72 | * @property {Function} set Set dynamic settings
73 | * @property {Function} delete .
74 | * @property {Function} available .
75 | *
76 | * @property {Function} registerNodeSettings : [Function: registerNodeSettings],
77 | * @property {Function} exportNodeSettings : [Function: exportNodeSettings],
78 | * @property {Function} enableNodeSettings : [Function: enableNodeSettings],
79 | * @property {Function} disableNodeSettings : [Function: disableNodeSettings],
80 | *
81 | * @property {Function} getUserSettings : [Function: getUserSettings],
82 | * @property {Function} setUserSettings : [Function: setUserSettings],
83 | */
84 |
85 | /** runtimeLogging
86 | * @typedef {object} runtimeLogging Logging. Levels that are output to the Node-RED log are controlled by the logging.console.level setting in settings.js
87 | * @property {Function} fatal Lvel 0. Lowest level, things that have broken Node-RED only.
88 | * @property {Function} error Level 1. Copy is sent to Editor debug panel as well as error log.
89 | * @property {Function} warn Level 2.
90 | * @property {Function} info Level 3.
91 | * @property {Function} debug Level 4.
92 | * @property {Function} trace Level 5. Very verbose output. Should tell the operator everything that is going on.
93 | * @property {Function} metric Log metrics (timings)
94 | * @property {Function} audit Audit log
95 | * @property {Function} addHandler Adds a log handler
96 | * @property {Function} removeHandler Removes a log handler
97 | */
98 |
99 | /** runtimeNodes
100 | * @typedef {object} runtimeNodes Gives access to other active nodes in the flows.
101 | * @property {Function} registerType Register a new type of node to Node-RED.
102 | * @property {Function} createNode Create a node instance (called from within registerType Function).
103 | * @property {Function} getNode Get a reference to another node instance in the current flows. Can then access its properties.
104 | * @property {Function} eachNode .
105 | * @property {Function} addCredentials .
106 | * @property {Function} getCredentials .
107 | * @property {Function} deleteCredentials .
108 | */
109 |
110 | /** runtimeRED
111 | * @typedef {object} runtimeRED The core Node-RED runtime object
112 | * @property {expressApp} httpAdmin Reference to the ExpressJS app for Node-RED Admin including the Editor
113 | * @property {expressApp} httpNode Reference to the ExpressJS app for Node-RED user-facing nodes including http-in/-out and Dashboard
114 | * @property {object} server Node.js http(s) Server object
115 | * @property {runtimeLogging} log Logging.
116 | * @property {runtimeNodes} nodes Gives access to other active nodes in the flows.
117 | * @property {runtimeSettings} settings Static and Dynamic settings for Node-RED runtime
118 | *
119 | * @property {Function} version Get the Node-RED version
120 | * @property {Function} require : [Function: requireModule],
121 | * @property {Function} comms : { publish: [Function: publish] },
122 | * @property {Function} library : { register: [Function: register] },
123 | * @property {Function} auth : { needsPermission: [Function: needsPermission] },
124 | *
125 | * @property {object} events Event handler object
126 | * @property {Function} events.on Event Listener Function. Types: 'nodes-started', 'nodes-stopped'
127 | * @property {Function} events.once .
128 | * @property {Function} events.addListener .
129 | *
130 | * @property {object} hooks .
131 | * @property {Function} hooks.has .
132 | * @property {Function} hooks.clear .
133 | * @property {Function} hooks.add .
134 | * @property {Function} hooks.remove .
135 | * @property {Function} hooks.trigger .
136 | *
137 | * @property {object} util .
138 | * @property {Function} util.encodeobject : [Function: encodeobject],
139 | * @property {Function} util.ensurestring : [Function: ensurestring],
140 | * @property {Function} util.ensureBuffer : [Function: ensureBuffer],
141 | * @property {Function} util.cloneMessage : [Function: cloneMessage],
142 | * @property {Function} util.compareobjects : [Function: compareobjects],
143 | * @property {Function} util.generateId : [Function: generateId],
144 | * @property {Function} util.getMessageProperty : [Function: getMessageProperty],
145 | * @property {Function} util.setMessageProperty : [Function: setMessageProperty],
146 | * @property {Function} util.getobjectProperty : [Function: getobjectProperty],
147 | * @property {Function} util.setobjectProperty : [Function: setobjectProperty],
148 | * @property {Function} util.evaluateNodeProperty : [Function: evaluateNodeProperty],
149 | * @property {Function} util.normalisePropertyExpression : [Function: normalisePropertyExpression],
150 | * @property {Function} util.normaliseNodeTypeName : [Function: normaliseNodeTypeName],
151 | * @property {Function} util.prepareJSONataExpression : [Function: prepareJSONataExpression],
152 | * @property {Function} util.evaluateJSONataExpression : [Function: evaluateJSONataExpression],
153 | * @property {Function} util.parseContextStore : [Function: parseContextStore]
154 | */
155 |
156 | /** runtimeNode
157 | * @typedef {object} runtimeNode Local copy of the node instance config + other info
158 | * @property {Function} send Send a Node-RED msg to an output port
159 | * @property {Function} done Dummy done Function for pre-Node-RED 1.0 servers
160 | * @property {Function} context get/set context data. Also .flow and .global contexts
161 | * @property {Function} on Event listeners for the node instance ('input', 'close')
162 | * @property {Function} removeListener Event handling
163 | * @property {Function} error Error log output, also logs to the Editor's debug panel
164 | * @property {object=} credentials Optional secured credentials
165 | * @property {object=} name Internal.
166 | * @property {object=} id Internal. uid of node instance.
167 | * @property {object=} type Internal. Type of node instance.
168 | * @property {object=} z Internal. uid of ???
169 | * @property {[Array]=} wires Internal. Array of Array of strings. The wires attached to this node instance (uid's)
170 | */
171 |
172 | /** runtimeNodeConfig
173 | * @typedef {object} runtimeNodeConfig Configuration of node instance. Will also have Editor panel's defined variables as properties.
174 | * @property {object=} id Internal. uid of node instance.
175 | * @property {object=} type Internal. Type of node instance.
176 | * @property {object=} x Internal
177 | * @property {object=} y Internal
178 | * @property {object=} z Internal
179 | * @property {object=} wires Internal. The wires attached to this node instance (uid's)
180 | */
181 |
182 | /** uibNode
183 | * @typedef {object} uibNode Local copy of the node instance config + other info
184 | * @property {object} uibNode .
185 | * @property {string} uibNode.id Unique identifier for this instance
186 | * @property {string} uibNode.type What type of node is this an instance of? (uibuilder)
187 | * @property {string} uibNode.name Descriptive name, only used by Editor
188 | * @property {string} uibNode.topic msg.topic overrides incoming msg.topic
189 | * @property {string} uibNode.url The url path (and folder path) to be used by this instance
190 | * @property {string} uibNode.oldUrl The PREVIOUS url path (and folder path) after a url rename
191 | * @property {boolean} uibNode.fwdInMessages Forward input msgs to output #1?
192 | * @property {boolean} uibNode.allowScripts Allow scripts to be sent to front-end via msg? WARNING: can be a security issue.
193 | * @property {boolean} uibNode.allowStyles Allow CSS to be sent to the front-end via msg? WARNING: can be a security issue.
194 | * @property {boolean} uibNode.copyIndex DEPRECATED Copy index.(html|js|css) files from templates if they don't exist?
195 | * @property {string} uibNode.templateFolder Folder name for the source of the chosen template
196 | * @property {string} uibNode.extTemplate Degit url reference for an external template (e.g. from GitHub)
197 | * @property {boolean} uibNode.showfolder Provide a folder index web page?
198 | * @property {boolean} uibNode.useSecurity Use uibuilder's built-in security features?
199 | * @property {boolean} uibNode.tokenAutoExtend Extend token life when msg's received from client?
200 | * @property {number} uibNode.sessionLength Lifespan of token (in seconds)
201 | * @property {boolean} uibNode.reload If true, notify all clients to reload on a change to any source file
202 | * @property {string} uibNode.sourceFolder (src or dist) the instance FE code folder to be served by ExpressJS
203 | * @property {string} uibNode.jwtSecret Seed string for encryption of JWT
204 | * @property {string} uibNode.customFolder Name of the fs path used to hold custom files & folders for THIS INSTANCE
205 | * @property {number} uibNode.ioClientsCount How many Socket clients connected to this instance?
206 | * @property {number} uibNode.rcvMsgCount How many msg's received since last reset or redeploy?
207 | * @property {object} uibNode.ioChannels The channel names for Socket.IO
208 | * @property {string} uibNode.ioChannels.control SIO Control channel name 'uiBuilderControl'
209 | * @property {string} uibNode.ioChannels.client SIO Client channel name 'uiBuilderClient'
210 | * @property {string} uibNode.ioChannels.server SIO Server channel name 'uiBuilder'
211 | * @property {string} uibNode.ioNamespace Make sure each node instance uses a separate Socket.IO namespace
212 | * @property {Function} uibNode.send Send a Node-RED msg to an output port
213 | * @property {Function=} uibNode.done Dummy done Function for pre-Node-RED 1.0 servers
214 | * @property {Function=} uibNode.on Event handler
215 | * @property {Function=} uibNode.removeListener Event handling
216 | * @property {object=} uibNode.credentials Optional secured credentials
217 | * @property {object=} uibNode.z Internal
218 | * @property {object=} uibNode.wires Internal. The wires attached to this node instance (uid's)
219 | *
220 | * @property {boolean} uibNode.commonStaticLoaded Whether the common static folder has been added
221 | * @property {boolean} uibNode.initCopyDone Has the initial template copy been done?
222 | *
223 | * @property {Function} uibNode.warn Output warn level info to node-red console and to editor debug
224 | */
225 |
226 | /** MsgAuth
227 | * @typedef {object} MsgAuth The standard auth object used by uibuilder security. See docs for details.
228 | * Note that any other data may be passed from your front-end code in the _auth.info object.
229 | * _auth.info.error, _auth.info.validJwt, _auth.info.message, _auth.info.warning
230 | * @property {object} MsgAuth .
231 | * @property {string} MsgAuth.id Required. A unique user identifier.
232 | * @property {string} [MsgAuth.password] Required for login only.
233 | * @property {string} [MsgAuth.jwt] Required if logged in. Needed for ongoing session validation and management.
234 | * @property {number} [MsgAuth.sessionExpiry] Required if logged in. Milliseconds since 1970. Needed for ongoing session validation and management.
235 | * @property {boolean} [MsgAuth.userValidated] Required after user validation. Whether the input ID (and optional additional data from the _auth object) validated correctly or not.
236 | * @property {object=} [MsgAuth.info] Optional metadata about the user.
237 | */
238 |
239 | /** userValidation
240 | * @typedef {object} userValidation Optional return object that is able to pass on additional use metadata back to the client.
241 | * @property {object} userValidation Optional return object that is able to pass on additional use metadata back to the client.
242 | * @property {boolean} userValidation.userValidated Required. Whether the input ID (and optional additional data from the _auth object) validated correctly or not.
243 | * @property {userMetadata} [userValidation.authData] Optional return metadata about the user. Will be added to the output msg's _auth object
244 | */
245 |
246 | /** userMetadata
247 | * @typedef {object} userMetadata Optional. Metadata about the user. Will be added to the output msg's _auth object.
248 | * This is just an example of what you might want to return, you can send anything you like.
249 | * @property {object} userMetadata Optional. Metadata about the user. Will be added to the output msg's _auth object.
250 | * @property {string} [userMetadata.name] Users full name or screen name.
251 | * @property {string} [userMetadata.message] A message that the front-end code could use to display to the user when they log in.
252 | * @property {string} [userMetadata.level] Users authorisation level (admin, gold, silver, reader, editor, author, ...).
253 | * @property {string} [userMetadata.location] Users location.
254 | * @property {Date} [userMetadata.passwordExpiry] Date/time the users password expires.
255 | * @property {Date} [userMetadata.subsExpiry] Date/time the users subscription expires.
256 | */
257 |
258 | /** Props define attributes on a virtual node.
259 | * @typedef {object. | {}} Props
260 | * @property {object} Props .
261 | * @property {Children} Props.children .
262 | */
263 | /** The vnode children of a virtual node.
264 | * @typedef {VNode[]} Children
265 | */
266 | /** Define a custom type for virtual nodes:
267 | * @typedef {string | number | Function} Type
268 | * @typedef {object.} VNode
269 | * @property {object.} VNode .
270 | * @property {Type} VNode.type .
271 | * @property {Props} VNode.props .
272 | * @property {Children} VNode.children .
273 | * @property {string} [VNode.key] .
274 | */
275 |
276 | // ==== vvv These need some work vvv ==== //
277 |
278 | // ExpressJS App
279 | /**
280 | * @typedef {object} expressApp ExpessJS `app` object
281 | * @property {object} _events : [object: null prototype] { mount: [Function: onmount] },
282 | * @property {number} _eventsCount : 1,
283 | * @property {number} _maxListeners : undefined,
284 | * @property {Function} setMaxListeners : [Function: setMaxListeners],
285 | * @property {Function} getMaxListeners : [Function: getMaxListeners],
286 | * @property {Function} emit : [Function: emit],
287 | * @property {Function} addListener : [Function: addListener],
288 | * @property {Function} on : [Function: addListener],
289 | * @property {Function} prependListener : [Function: prependListener],
290 | * @property {Function} once : [Function: once],
291 | * @property {Function} prependOnceListener : [Function: prependOnceListener],
292 | * @property {Function} removeListener : [Function: removeListener],
293 | * @property {Function} off : [Function: removeListener],
294 | * @property {Function} removeAllListeners : [Function: removeAllListeners],
295 | * @property {Function} listeners : [Function: listeners],
296 | * @property {Function} rawListeners : [Function: rawListeners],
297 | * @property {Function} listenerCount : [Function: listenerCount],
298 | * @property {Function} eventNames : [Function: eventNames],
299 | * @property {Function} init : [Function: init],
300 | * @property {Function} defaultConfiguration : [Function: defaultConfiguration],
301 | * @property {Function} lazyrouter : [Function: lazyrouter],
302 | * @property {Function} handle : [Function: handle],
303 | * @property {Function} use : [Function: use],
304 | * @property {Function} route : [Function: route],
305 | * @property {Function} engine : [Function: engine],
306 | * @property {Function} param : [Function: param],
307 | * @property {Function} set : [Function: set],
308 | * @property {Function} path : [Function: path],
309 | * @property {Function} enabled : [Function: enabled],
310 | * @property {Function} disabled : [Function: disabled],
311 | * @property {Function} enable : [Function: enable],
312 | * @property {Function} disable : [Function: disable],
313 | * @property {Function} acl : [Function (anonymous)],
314 | * @property {Function} bind : [Function (anonymous)],
315 | * @property {Function} checkout : [Function (anonymous)],
316 | * @property {Function} connect : [Function (anonymous)],
317 | * @property {Function} copy : [Function (anonymous)],
318 | * @property {Function} delete : [Function (anonymous)],
319 | * @property {Function} get : [Function (anonymous)],
320 | * @property {Function} head : [Function (anonymous)],
321 | * @property {Function} link : [Function (anonymous)],
322 | * @property {Function} lock : [Function (anonymous)],
323 | * @property {Function} 'm-search' : [Function (anonymous)],
324 | * @property {Function} merge : [Function (anonymous)],
325 | * @property {Function} mkactivity : [Function (anonymous)],
326 | * @property {Function} mkcalendar : [Function (anonymous)],
327 | * @property {Function} mkcol : [Function (anonymous)],
328 | * @property {Function} move : [Function (anonymous)],
329 | * @property {Function} notify : [Function (anonymous)],
330 | * @property {Function} options : [Function (anonymous)],
331 | * @property {Function} patch : [Function (anonymous)],
332 | * @property {Function} post : [Function (anonymous)],
333 | * @property {Function} pri : [Function (anonymous)],
334 | * @property {Function} propfind : [Function (anonymous)],
335 | * @property {Function} proppatch : [Function (anonymous)],
336 | * @property {Function} purge : [Function (anonymous)],
337 | * @property {Function} put : [Function (anonymous)],
338 | * @property {Function} rebind : [Function (anonymous)],
339 | * @property {Function} report : [Function (anonymous)],
340 | * @property {Function} search : [Function (anonymous)],
341 | * @property {Function} source : [Function (anonymous)],
342 | * @property {Function} subscribe : [Function (anonymous)],
343 | * @property {Function} trace : [Function (anonymous)],
344 | * @property {Function} unbind : [Function (anonymous)],
345 | * @property {Function} unlink : [Function (anonymous)],
346 | * @property {Function} unlock : [Function (anonymous)],
347 | * @property {Function} unsubscribe : [Function (anonymous)],
348 | * @property {Function} all : [Function: all],
349 | * @property {Function} del : [Function (anonymous)],
350 | * @property {Function} render : [Function: render],
351 | * @property {Function} listen : [Function: listen],
352 | * @property {Function} request : IncomingMessage { app: [Circular *1] },
353 | * @property {Function} response : ServerResponse { app: [Circular *1] },
354 | * @property {object} cache : {},
355 | * @property {object} engines : {},
356 | *
357 | * @property {object} settings : {
358 | * @property {boolean} settings.'x-powered-by' : true,
359 | * @property {string} settings.etag : 'weak',
360 | * @property {Function} settings."etag fn" : [Function: generateETag],
361 | * @property {string} settings.env : 'development',
362 | * @property {string} settings.'query parser' : 'extended',
363 | * @property {Function} settings.'query parser fn' : [Function: parseExtendedQuerystring],
364 | * @property {number} settings.'subdomain offset' : 2,
365 | * @property {Function} settings.view : [Function: View],
366 | * @property {string} settings.views : 'C:\\src\\nr2\\views',
367 | * @property {string} settings.'jsonp callback name' : 'callback'
368 | *
369 | * @property {object} locals : [object: null prototype] { settings: [object] },
370 | * @property {string} mountpath : '/nr/',
371 | *
372 | * @property {Function} parent : [Function: app] {
373 | * @property {Function} parent._events : [object: null prototype],
374 | * @property {Function} parent._eventsCount : 1,
375 | * @property {Function} parent._maxListeners : undefined,
376 | * @property {Function} parent.setMaxListeners : [Function: setMaxListeners],
377 | * @property {Function} parent.getMaxListeners : [Function: getMaxListeners],
378 | * @property {Function} parent.emit : [Function: emit],
379 | * @property {Function} parent.addListener : [Function: addListener],
380 | * @property {Function} parent.on : [Function: addListener],
381 | * @property {Function} parent.prependListener : [Function: prependListener],
382 | * @property {Function} parent.once : [Function: once],
383 | * @property {Function} parent.prependOnceListener : [Function: prependOnceListener],
384 | * @property {Function} parent.removeListener : [Function: removeListener],
385 | * @property {Function} parent.off : [Function: removeListener],
386 | * @property {Function} parent.removeAllListeners : [Function: removeAllListeners],
387 | * @property {Function} parent.listeners : [Function: listeners],
388 | * @property {Function} parent.rawListeners : [Function: rawListeners],
389 | * @property {Function} parent.listenerCount : [Function: listenerCount],
390 | * @property {Function} parent.eventNames : [Function: eventNames],
391 | * @property {Function} parent.init : [Function: init],
392 | * @property {Function} parent.defaultConfiguration : [Function: defaultConfiguration],
393 | * @property {Function} parent.lazyrouter : [Function: lazyrouter],
394 | * @property {Function} parent.handle : [Function: handle],
395 | * @property {Function} parent.use : [Function: use],
396 | * @property {Function} parent.route : [Function: route],
397 | * @property {Function} parent.engine : [Function: engine],
398 | * @property {Function} parent.param : [Function: param],
399 | * @property {Function} parent.set : [Function: set],
400 | * @property {Function} parent.path : [Function: path],
401 | * @property {Function} parent.enabled : [Function: enabled],
402 | * @property {Function} parent.disabled : [Function: disabled],
403 | * @property {Function} parent.enable : [Function: enable],
404 | * @property {Function} parent.disable : [Function: disable],
405 | * @property {Function} parent.acl : [Function (anonymous)],
406 | * @property {Function} parent.bind : [Function (anonymous)],
407 | * @property {Function} parent.checkout : [Function (anonymous)],
408 | * @property {Function} parent.connect : [Function (anonymous)],
409 | * @property {Function} parent.copy : [Function (anonymous)],
410 | * @property {Function} parent.delete : [Function (anonymous)],
411 | * @property {Function} parent.get : [Function (anonymous)],
412 | * @property {Function} parent.head : [Function (anonymous)],
413 | * @property {Function} parent.link : [Function (anonymous)],
414 | * @property {Function} parent.lock : [Function (anonymous)],
415 | * @property {Function} parent.'m-search' : [Function (anonymous)],
416 | * @property {Function} parent.merge : [Function (anonymous)],
417 | * @property {Function} parent.mkactivity : [Function (anonymous)],
418 | * @property {Function} locals : [object: null prototype],
419 | * @property {Function} mountpath : '/',
420 | * @property {Function} _router : [Function]
421 | */
422 |
423 | module.exports = {}
424 |
425 | /* RED
426 | {
427 | "loader": {},
428 | "events": {},
429 | "i18n": {},
430 | "settings": {
431 | "apiRootUrl": "",
432 | "httpNodeRoot": "/nr/",
433 | "version": "1.2.7",
434 | "user": {
435 | "anonymous": true,
436 | "permissions": "*"
437 | },
438 | "context": {
439 | "default": "default",
440 | "stores": [
441 | "default",
442 | "file"
443 | ]
444 | },
445 | "flowFilePretty": true,
446 | "flowEncryptionType": "user",
447 | "tlsConfigDisableLocalFiles": false,
448 | "uibuilderNodeEnv": "development", // === CUSTOM ===
449 | "uibuilderTemplates": {}, // === CUSTOM ===
450 | "uibuilderPort": 3000, // === CUSTOM ===
451 | "editorTheme": {},
452 |
453 | "get": Function,
454 | init: Function,
455 | load: Function,
456 | loadUserSettings: Function,
457 | remove: Function,
458 | set: Function,
459 | theme: Function,
460 | "user": {},
461 | "comms": {},
462 | "text": {},
463 | "state": {},
464 | "nodes": {},
465 | "history": {},
466 | "validators": {},
467 | "utils": {},
468 | "menu": {},
469 | "panels": {},
470 | "popover": {},
471 | "tabs": {},
472 | "stack": {},
473 | "colorPicker": {},
474 | "actions": {},
475 | "deploy": {},
476 | "diff": {},
477 | "keyboard": {},
478 | "workspaces": {},
479 | "statusBar": {},
480 | "view": {
481 | "navigator": {},
482 | "tools": {}
483 | },
484 | "sidebar": {},
485 | "palette": {},
486 | "editor": {},
487 | "eventLog": {},
488 | "tray": {},
489 | "clipboard": {},
490 | "library": {},
491 | "notifications": {},
492 | "search": {},
493 | "actionList": {},
494 | "typeSearch": {},
495 | "subflow": {},
496 | "group": {},
497 | "userSettings": {},
498 | "projects": {},
499 | "touch": {},
500 | "debug": {}
501 | }
502 | */
503 | /* this
504 | {
505 | name: ""
506 | topic: ""
507 | //... other vars ...//
508 |
509 | credentials: { has_jwtSecret: false, _: { … } }
510 |
511 | changed: false
512 | dirty: false
513 | icon: undefined
514 | id: "b18a50dd.f7e5c"
515 | info: undefined
516 | infoEditor: w { $toDestroy: Array(46), container: div.red - ui - editor - text - container.ace_editor.ace_hidpi.red - ui - editor - text - container - toolbar.ace - tomo…, renderer: y, id: "editor2", commands: o, … }
517 | inputLabels: ""
518 | inputs: 1
519 | outputLabels: ['','']
520 | outputs: 2
521 | resize: false
522 | selected: true
523 | status: { text: "Node Initialised", fill: "blue", shape: "dot" }
524 | type: "uibuilder"
525 | valid: true
526 | validationErrors: []
527 |
528 | g: "c49c82f3.7e716"
529 | h: 30
530 | l: true
531 | w: 120
532 | x: 530
533 | y: 120
534 | z: "18cb249f.38bafb"
535 |
536 | _: ƒ()
537 | __outputs: 2
538 | _config: { name: """", topic: """", url: ""vue - file"", fwdInMessages: "false", allowScripts: "false", … }
539 | _def: { category: "uibuilder", color: "#E6E0F8", defaults: { … }, credentials: { … }, inputs: 1, … }
540 | }
541 | */
542 |
--------------------------------------------------------------------------------