node-red-contrib-room-tools 1.8.4 → 1.8.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,433 @@
1
+ {
2
+ "name": "node-red-contrib-room-tools",
3
+ "version": "1.8.8",
4
+ "lockfileVersion": 2,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "node-red-contrib-room-tools",
9
+ "version": "1.8.8",
10
+ "license": "UNLICENSED",
11
+ "dependencies": {
12
+ "@vue/compiler-sfc": "3.5.42",
13
+ "acorn": "8.18.0",
14
+ "escodegen": "2.1.0"
15
+ },
16
+ "engines": {
17
+ "node": ">=18.16.0"
18
+ }
19
+ },
20
+ "node_modules/@babel/helper-string-parser": {
21
+ "version": "7.29.7",
22
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
23
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
24
+ "engines": {
25
+ "node": ">=6.9.0"
26
+ }
27
+ },
28
+ "node_modules/@babel/helper-validator-identifier": {
29
+ "version": "7.29.7",
30
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
31
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
32
+ "engines": {
33
+ "node": ">=6.9.0"
34
+ }
35
+ },
36
+ "node_modules/@babel/parser": {
37
+ "version": "7.29.9",
38
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz",
39
+ "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==",
40
+ "dependencies": {
41
+ "@babel/types": "^7.29.8"
42
+ },
43
+ "bin": {
44
+ "parser": "bin/babel-parser.js"
45
+ },
46
+ "engines": {
47
+ "node": ">=6.0.0"
48
+ }
49
+ },
50
+ "node_modules/@babel/types": {
51
+ "version": "7.29.8",
52
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
53
+ "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
54
+ "dependencies": {
55
+ "@babel/helper-string-parser": "^7.29.7",
56
+ "@babel/helper-validator-identifier": "^7.29.7"
57
+ },
58
+ "engines": {
59
+ "node": ">=6.9.0"
60
+ }
61
+ },
62
+ "node_modules/@jridgewell/sourcemap-codec": {
63
+ "version": "1.6.0",
64
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz",
65
+ "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="
66
+ },
67
+ "node_modules/@vue/compiler-core": {
68
+ "version": "3.5.42",
69
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.42.tgz",
70
+ "integrity": "sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==",
71
+ "dependencies": {
72
+ "@babel/parser": "^7.29.8",
73
+ "@vue/shared": "3.5.42",
74
+ "entities": "^7.0.1",
75
+ "estree-walker": "^2.0.2",
76
+ "source-map-js": "^1.2.1"
77
+ }
78
+ },
79
+ "node_modules/@vue/compiler-dom": {
80
+ "version": "3.5.42",
81
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.42.tgz",
82
+ "integrity": "sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==",
83
+ "dependencies": {
84
+ "@vue/compiler-core": "3.5.42",
85
+ "@vue/shared": "3.5.42"
86
+ }
87
+ },
88
+ "node_modules/@vue/compiler-sfc": {
89
+ "version": "3.5.42",
90
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.42.tgz",
91
+ "integrity": "sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==",
92
+ "dependencies": {
93
+ "@babel/parser": "^7.29.8",
94
+ "@vue/compiler-core": "3.5.42",
95
+ "@vue/compiler-dom": "3.5.42",
96
+ "@vue/compiler-ssr": "3.5.42",
97
+ "@vue/shared": "3.5.42",
98
+ "estree-walker": "^2.0.2",
99
+ "magic-string": "^0.30.21",
100
+ "postcss": "^8.5.19",
101
+ "source-map-js": "^1.2.1"
102
+ }
103
+ },
104
+ "node_modules/@vue/compiler-ssr": {
105
+ "version": "3.5.42",
106
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.42.tgz",
107
+ "integrity": "sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==",
108
+ "dependencies": {
109
+ "@vue/compiler-dom": "3.5.42",
110
+ "@vue/shared": "3.5.42"
111
+ }
112
+ },
113
+ "node_modules/@vue/shared": {
114
+ "version": "3.5.42",
115
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.42.tgz",
116
+ "integrity": "sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g=="
117
+ },
118
+ "node_modules/acorn": {
119
+ "version": "8.18.0",
120
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz",
121
+ "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==",
122
+ "bin": {
123
+ "acorn": "bin/acorn"
124
+ },
125
+ "engines": {
126
+ "node": ">=0.4.0"
127
+ }
128
+ },
129
+ "node_modules/entities": {
130
+ "version": "7.0.1",
131
+ "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
132
+ "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
133
+ "engines": {
134
+ "node": ">=0.12"
135
+ },
136
+ "funding": {
137
+ "url": "https://github.com/fb55/entities?sponsor=1"
138
+ }
139
+ },
140
+ "node_modules/escodegen": {
141
+ "version": "2.1.0",
142
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
143
+ "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
144
+ "dependencies": {
145
+ "esprima": "^4.0.1",
146
+ "estraverse": "^5.2.0",
147
+ "esutils": "^2.0.2"
148
+ },
149
+ "bin": {
150
+ "escodegen": "bin/escodegen.js",
151
+ "esgenerate": "bin/esgenerate.js"
152
+ },
153
+ "engines": {
154
+ "node": ">=6.0"
155
+ },
156
+ "optionalDependencies": {
157
+ "source-map": "~0.6.1"
158
+ }
159
+ },
160
+ "node_modules/esprima": {
161
+ "version": "4.0.1",
162
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
163
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
164
+ "bin": {
165
+ "esparse": "bin/esparse.js",
166
+ "esvalidate": "bin/esvalidate.js"
167
+ },
168
+ "engines": {
169
+ "node": ">=4"
170
+ }
171
+ },
172
+ "node_modules/estraverse": {
173
+ "version": "5.3.0",
174
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
175
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
176
+ "engines": {
177
+ "node": ">=4.0"
178
+ }
179
+ },
180
+ "node_modules/estree-walker": {
181
+ "version": "2.0.2",
182
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
183
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
184
+ },
185
+ "node_modules/esutils": {
186
+ "version": "2.0.3",
187
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
188
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
189
+ "engines": {
190
+ "node": ">=0.10.0"
191
+ }
192
+ },
193
+ "node_modules/magic-string": {
194
+ "version": "0.30.21",
195
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
196
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
197
+ "dependencies": {
198
+ "@jridgewell/sourcemap-codec": "^1.5.5"
199
+ }
200
+ },
201
+ "node_modules/nanoid": {
202
+ "version": "3.3.19",
203
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz",
204
+ "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==",
205
+ "funding": [
206
+ {
207
+ "type": "github",
208
+ "url": "https://github.com/sponsors/ai"
209
+ }
210
+ ],
211
+ "bin": {
212
+ "nanoid": "bin/nanoid.cjs"
213
+ },
214
+ "engines": {
215
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
216
+ }
217
+ },
218
+ "node_modules/picocolors": {
219
+ "version": "1.1.1",
220
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
221
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
222
+ },
223
+ "node_modules/postcss": {
224
+ "version": "8.5.28",
225
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz",
226
+ "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==",
227
+ "funding": [
228
+ {
229
+ "type": "opencollective",
230
+ "url": "https://opencollective.com/postcss/"
231
+ },
232
+ {
233
+ "type": "tidelift",
234
+ "url": "https://tidelift.com/funding/github/npm/postcss"
235
+ },
236
+ {
237
+ "type": "github",
238
+ "url": "https://github.com/sponsors/ai"
239
+ }
240
+ ],
241
+ "dependencies": {
242
+ "nanoid": "^3.3.18",
243
+ "picocolors": "^1.1.1",
244
+ "source-map-js": "^1.2.1"
245
+ },
246
+ "engines": {
247
+ "node": "^10 || ^12 || >=14"
248
+ }
249
+ },
250
+ "node_modules/source-map": {
251
+ "version": "0.6.1",
252
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
253
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
254
+ "optional": true,
255
+ "engines": {
256
+ "node": ">=0.10.0"
257
+ }
258
+ },
259
+ "node_modules/source-map-js": {
260
+ "version": "1.2.1",
261
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
262
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
263
+ "engines": {
264
+ "node": ">=0.10.0"
265
+ }
266
+ }
267
+ },
268
+ "dependencies": {
269
+ "@babel/helper-string-parser": {
270
+ "version": "7.29.7",
271
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
272
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="
273
+ },
274
+ "@babel/helper-validator-identifier": {
275
+ "version": "7.29.7",
276
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
277
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="
278
+ },
279
+ "@babel/parser": {
280
+ "version": "7.29.9",
281
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz",
282
+ "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==",
283
+ "requires": {
284
+ "@babel/types": "^7.29.8"
285
+ }
286
+ },
287
+ "@babel/types": {
288
+ "version": "7.29.8",
289
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
290
+ "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
291
+ "requires": {
292
+ "@babel/helper-string-parser": "^7.29.7",
293
+ "@babel/helper-validator-identifier": "^7.29.7"
294
+ }
295
+ },
296
+ "@jridgewell/sourcemap-codec": {
297
+ "version": "1.6.0",
298
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz",
299
+ "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="
300
+ },
301
+ "@vue/compiler-core": {
302
+ "version": "3.5.42",
303
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.42.tgz",
304
+ "integrity": "sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==",
305
+ "requires": {
306
+ "@babel/parser": "^7.29.8",
307
+ "@vue/shared": "3.5.42",
308
+ "entities": "^7.0.1",
309
+ "estree-walker": "^2.0.2",
310
+ "source-map-js": "^1.2.1"
311
+ }
312
+ },
313
+ "@vue/compiler-dom": {
314
+ "version": "3.5.42",
315
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.42.tgz",
316
+ "integrity": "sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==",
317
+ "requires": {
318
+ "@vue/compiler-core": "3.5.42",
319
+ "@vue/shared": "3.5.42"
320
+ }
321
+ },
322
+ "@vue/compiler-sfc": {
323
+ "version": "3.5.42",
324
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.42.tgz",
325
+ "integrity": "sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==",
326
+ "requires": {
327
+ "@babel/parser": "^7.29.8",
328
+ "@vue/compiler-core": "3.5.42",
329
+ "@vue/compiler-dom": "3.5.42",
330
+ "@vue/compiler-ssr": "3.5.42",
331
+ "@vue/shared": "3.5.42",
332
+ "estree-walker": "^2.0.2",
333
+ "magic-string": "^0.30.21",
334
+ "postcss": "^8.5.19",
335
+ "source-map-js": "^1.2.1"
336
+ }
337
+ },
338
+ "@vue/compiler-ssr": {
339
+ "version": "3.5.42",
340
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.42.tgz",
341
+ "integrity": "sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==",
342
+ "requires": {
343
+ "@vue/compiler-dom": "3.5.42",
344
+ "@vue/shared": "3.5.42"
345
+ }
346
+ },
347
+ "@vue/shared": {
348
+ "version": "3.5.42",
349
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.42.tgz",
350
+ "integrity": "sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g=="
351
+ },
352
+ "acorn": {
353
+ "version": "8.18.0",
354
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz",
355
+ "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="
356
+ },
357
+ "entities": {
358
+ "version": "7.0.1",
359
+ "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
360
+ "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="
361
+ },
362
+ "escodegen": {
363
+ "version": "2.1.0",
364
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
365
+ "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
366
+ "requires": {
367
+ "esprima": "^4.0.1",
368
+ "estraverse": "^5.2.0",
369
+ "esutils": "^2.0.2",
370
+ "source-map": "~0.6.1"
371
+ }
372
+ },
373
+ "esprima": {
374
+ "version": "4.0.1",
375
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
376
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
377
+ },
378
+ "estraverse": {
379
+ "version": "5.3.0",
380
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
381
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="
382
+ },
383
+ "estree-walker": {
384
+ "version": "2.0.2",
385
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
386
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
387
+ },
388
+ "esutils": {
389
+ "version": "2.0.3",
390
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
391
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
392
+ },
393
+ "magic-string": {
394
+ "version": "0.30.21",
395
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
396
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
397
+ "requires": {
398
+ "@jridgewell/sourcemap-codec": "^1.5.5"
399
+ }
400
+ },
401
+ "nanoid": {
402
+ "version": "3.3.19",
403
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz",
404
+ "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug=="
405
+ },
406
+ "picocolors": {
407
+ "version": "1.1.1",
408
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
409
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
410
+ },
411
+ "postcss": {
412
+ "version": "8.5.28",
413
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz",
414
+ "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==",
415
+ "requires": {
416
+ "nanoid": "^3.3.18",
417
+ "picocolors": "^1.1.1",
418
+ "source-map-js": "^1.2.1"
419
+ }
420
+ },
421
+ "source-map": {
422
+ "version": "0.6.1",
423
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
424
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
425
+ "optional": true
426
+ },
427
+ "source-map-js": {
428
+ "version": "1.2.1",
429
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
430
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
431
+ }
432
+ }
433
+ }
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "node-red-contrib-room-tools",
3
- "version": "1.8.4",
4
- "description": "Room panel message generator and authenticated region gateway for Node-RED and MUSE Automator",
3
+ "version": "1.8.8",
4
+ "description": "Authenticated visual control panel binding for Node-RED and MUSE Automator",
5
5
  "license": "UNLICENSED",
6
+ "engines": { "node": ">=18.16.0" },
6
7
  "keywords": [
7
8
  "node-red"
8
9
  ],
9
10
  "dependencies": {
10
- "@vue/compiler-sfc": "^3.5.42",
11
- "acorn": "^8.15.0",
12
- "escodegen": "^2.1.0"
11
+ "@vue/compiler-sfc": "3.5.42",
12
+ "acorn": "8.18.0",
13
+ "escodegen": "2.1.0"
13
14
  },
14
15
  "files": [
15
16
  "assets/panel-login-bg.png",
@@ -24,12 +25,14 @@
24
25
  "panel-visual-bindings.js",
25
26
  "panel-visual-apply.js",
26
27
  "panel-editor-auth.js",
28
+ "panel-http.js",
27
29
  "panel-binding-editor.js",
28
30
  "native-binding-plan.js",
29
31
  "managed-native-bridge.js",
30
32
  "managed-bridge-revision.js",
31
33
  "managed-bridge-admin.js",
32
34
  "control-policy.js",
35
+ "npm-shrinkwrap.json",
33
36
  "CONTROL_PANEL_AI_GUIDE.md",
34
37
  "README.md"
35
38
  ],
@@ -2,6 +2,7 @@ function initializeVisualBinding () {
2
2
  const component = this
3
3
  const config = this.__boundPanelConfig()?.visual
4
4
  if (!config || !this.$el || typeof document === 'undefined') return
5
+ const matcher = component.__createTargetMatcher(config.contract)
5
6
  const root = this.$el
6
7
  const base = config.editorEndpoint
7
8
  const saved = new Map(Object.entries(config.bindings || {}))
@@ -47,43 +48,18 @@ function initializeVisualBinding () {
47
48
  }
48
49
  }
49
50
  async function request (path, method = 'GET', body) {
50
- const response = await fetch(base + path, {
51
+ const response = await component.__panelFetch(base + path, {
51
52
  method, credentials: 'same-origin',
52
53
  headers: method === 'POST'
53
54
  ? { 'Content-Type': 'application/json', 'X-Panel-CSRF': editSession?.csrf || '' }
54
55
  : undefined,
55
- body: method === 'POST' ? JSON.stringify(body || {}) : undefined,
56
- signal: AbortSignal.timeout(12000)
57
- })
56
+ body: method === 'POST' ? JSON.stringify(body || {}) : undefined
57
+ }, path === '/apply' ? 120000 : 12000)
58
58
  const result = await response.json().catch(() => ({}))
59
59
  if (!response.ok) throw new Error(result.error || `Request failed (${response.status})`)
60
60
  return result
61
61
  }
62
- function family (target) {
63
- if (target.controlSchemaVersion !== config.contract.schemaVersion) return null
64
- const known = config.contract.types[String(target.controlType || '').toLowerCase()]
65
- if (!known) return null
66
- const type = target.type
67
- if (known.family === 'number' && ['integer', 'float'].includes(type)) return 'number'
68
- if (known.family === 'boolean' && (type === 'boolean' ||
69
- (type === 'integer' && target.booleanEncoding === 'integer01' &&
70
- target.min === 0 && target.max === 1))) return 'boolean'
71
- if (known.family === 'text' && ['string', 'dstring'].includes(type)) return 'text'
72
- if (known.family === 'enum' && ['enum', 'integer', 'string'].includes(type)) return 'enum'
73
- return null
74
- }
75
- function compatible (adapter, target, direction, candidate) {
76
- const families = {
77
- range: ['number'], switch: ['boolean'], meter: ['number'],
78
- 'text-display': ['number', 'boolean', 'text', 'enum'],
79
- 'text-input': ['text'], select: ['enum']
80
- }
81
- if (target.kind !== 'param' || !families[adapter]?.includes(family(target))) return false
82
- if (direction === 'write' && ['meter', 'text-display'].includes(adapter)) return false
83
- if (candidate?.unit && candidate.unit !== target.unit) return false
84
- return direction === 'write' ? ['wo', 'rw'].includes(target.access) : ['ro', 'rw'].includes(target.access)
85
- }
86
- function numericRange (binding, node) {
62
+ function numericRange (binding, node) {
87
63
  if (binding.uiRange) return binding.uiRange
88
64
  const tag = node.tagName?.toLowerCase()
89
65
  const type = node.type?.toLowerCase()
@@ -390,7 +366,7 @@ function initializeVisualBinding () {
390
366
  }
391
367
  function loginView () {
392
368
  showLoginForm('ControlPanel editor', async (username, password) => {
393
- const response = await fetch(base + '/login', {
369
+ const response = await component.__panelFetch(base + '/login', {
394
370
  method: 'POST', credentials: 'same-origin',
395
371
  headers: { 'Content-Type': 'application/json' },
396
372
  body: JSON.stringify({ username, password })
@@ -456,9 +432,11 @@ function initializeVisualBinding () {
456
432
  const message = element('p', '', 'cpv-message')
457
433
  form.append(message)
458
434
  let targets = []
435
+ let targetGeneration = 0
459
436
  function updateTargets () {
460
- const oldWrite = write.value || existing?.writePath
461
- const oldRead = read.value || existing?.readPath || existing?.writePath
437
+ const sameDevice = existing?.device === device.value
438
+ const oldWrite = write.value || (sameDevice ? existing?.writePath : '')
439
+ const oldRead = read.value || (sameDevice ? existing?.readPath || existing?.writePath : '')
462
440
  clear(write); clear(read)
463
441
  write.append(new Option('Choose...', ''))
464
442
  read.append(new Option('Choose...', ''))
@@ -466,11 +444,11 @@ function initializeVisualBinding () {
466
444
  let readCount = 0
467
445
  for (const target of targets) {
468
446
  const label = `${target.path} (${target.type})`
469
- if (compatible(candidate.adapter, target, 'write', candidate)) {
447
+ if (matcher.compatible(candidate.adapter, target, 'write', candidate)) {
470
448
  write.append(new Option(label, target.path))
471
449
  writeCount++
472
450
  }
473
- if (compatible(candidate.adapter, target, 'read', candidate)) {
451
+ if (matcher.compatible(candidate.adapter, target, 'read', candidate)) {
474
452
  read.append(new Option(label, target.path))
475
453
  readCount++
476
454
  }
@@ -485,11 +463,22 @@ function initializeVisualBinding () {
485
463
  : targets.length ? '' : 'No points available'
486
464
  }
487
465
  device.addEventListener('change', async () => {
466
+ const generation = ++targetGeneration
467
+ const selectedDevice = device.value
468
+ targets = []
469
+ write.value = ''; read.value = ''
470
+ updateTargets()
488
471
  message.textContent = 'Loading points...'
489
472
  try {
490
- targets = device.value ? (await request('/targets?device=' + encodeURIComponent(device.value))).targets : []
473
+ const result = selectedDevice
474
+ ? (await request('/targets?device=' + encodeURIComponent(selectedDevice))).targets : []
475
+ if (generation !== targetGeneration) return
476
+ targets = result
491
477
  updateTargets()
492
- } catch (error) { targets = []; updateTargets(); message.textContent = error.message }
478
+ } catch (error) {
479
+ if (generation !== targetGeneration) return
480
+ targets = []; updateTargets(); message.textContent = error.message
481
+ }
493
482
  })
494
483
  write.addEventListener('change', () => { read.value = write.value })
495
484
  mode.addEventListener('change', updateTargets)
@@ -1,4 +1,5 @@
1
1
  const crypto = require('node:crypto')
2
+ const { readJsonBody, replyJson: reply, sameOrigin } = require('./panel-http')
2
3
 
3
4
  function createPanelEditor ({ panelId, username, password, secureCookie, getConfig, getDevices, getTargets, apply, restoredSessions }) {
4
5
  if (!username || !password) return null
@@ -20,47 +21,10 @@ function createPanelEditor ({ panelId, username, password, secureCookie, getConf
20
21
  const ttl = 30 * 60 * 1000
21
22
  let closed = false
22
23
 
23
- function reply (res, status, data, headers = {}) {
24
- res.writeHead(status, {
25
- 'Content-Type': 'application/json; charset=utf-8',
26
- 'Cache-Control': 'no-store',
27
- 'X-Content-Type-Options': 'nosniff',
28
- ...headers
29
- })
30
- res.end(JSON.stringify(data))
31
- }
32
-
33
24
  function cookie (value, age, req) {
34
25
  return `${cookieName}=${value}; Path=${prefix}; HttpOnly; SameSite=Strict; Max-Age=${age}${secureCookie && req.socket.encrypted ? '; Secure' : ''}`
35
26
  }
36
27
 
37
- function sameOrigin (req) {
38
- const origin = req.headers.origin
39
- if (!origin) return false
40
- try {
41
- const parsed = new URL(origin)
42
- return parsed.host === req.headers.host &&
43
- parsed.protocol === (req.socket.encrypted ? 'https:' : 'http:')
44
- } catch (_) { return false }
45
- }
46
-
47
- async function jsonBody (req) {
48
- if (!String(req.headers['content-type'] || '').startsWith('application/json')) {
49
- throw new Error('JSON body required')
50
- }
51
- if (req.body !== undefined) {
52
- if (Buffer.isBuffer(req.body)) return JSON.parse(req.body.toString('utf8'))
53
- if (typeof req.body === 'string') return JSON.parse(req.body)
54
- if (typeof req.body === 'object' && req.body !== null && Object.keys(req.body).length) return req.body
55
- }
56
- let raw = ''
57
- for await (const chunk of req) {
58
- raw += chunk
59
- if (Buffer.byteLength(raw) > 65536) throw new Error('Request too large')
60
- }
61
- return JSON.parse(raw)
62
- }
63
-
64
28
  async function handler (req, res) {
65
29
  const path = new URL(req.url, 'http://localhost').pathname
66
30
  if (!path.startsWith(prefix + '/')) return false
@@ -82,7 +46,7 @@ function createPanelEditor ({ panelId, username, password, secureCookie, getConf
82
46
  }
83
47
  attempt.count++
84
48
  attempts.set(ip, attempt)
85
- const body = await jsonBody(req)
49
+ const body = await readJsonBody(req, 65536)
86
50
  const supplied = String(body.password || '')
87
51
  if (supplied.length > 256) { reply(res, 401, { error: 'Invalid credentials' }); return true }
88
52
  const test = await new Promise((resolve, reject) => crypto.scrypt(supplied, salt, 32,
@@ -120,7 +84,7 @@ function createPanelEditor ({ panelId, username, password, secureCookie, getConf
120
84
  const device = new URL(req.url, 'http://localhost').searchParams.get('device')
121
85
  reply(res, 200, { targets: await getTargets(req, device) })
122
86
  } else if (path === prefix + '/apply' && req.method === 'POST') {
123
- reply(res, 200, await apply(req, await jsonBody(req)))
87
+ reply(res, 200, await apply(req, await readJsonBody(req, 65536)))
124
88
  } else {
125
89
  reply(res, 404, { error: 'Not found' })
126
90
  }