iobroker.javascript 9.2.2 → 9.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -11
- package/admin/assets/{AiChatPanel-Bbux7LP7.js → AiChatPanel-C4zUsNmN.js} +1 -1
- package/admin/assets/{ScriptEditor-DKljeYQN.js → ScriptEditor-C6W68ya0.js} +1 -1
- package/admin/assets/ScriptEditorVanillaMonaco-DYTcDqp_.js +3 -0
- package/admin/assets/cronHoverProvider-Bg0Vz-1E.js +2 -0
- package/admin/assets/index-45IwpC7_.js +1 -0
- package/admin/assets/{index-_BUuZlI0.js → index-BXfqctma.js} +1 -1
- package/admin/assets/{index-DSvAO584.js → index-CUau9-il.js} +83 -83
- package/admin/assets/{index-DgE6XhUM.js → index-crBQrXKG.js} +1 -1
- package/admin/assets/{index-CexrwGGC.js → index-qoSy77Bg.js} +41 -37
- package/admin/assets/{localSharedImportMap-BCwqnmn4.js → localSharedImportMap-CVlAGiIu.js} +1 -1
- package/admin/assets/stateHoverProvider-BYBqzJuX.js +9 -0
- package/admin/assets/{virtual_mf-REMOTE_ENTRY_ID_iobroker_javascript__remoteEntry_js-DNrEsMrg.js → virtual_mf-REMOTE_ENTRY_ID_iobroker_javascript__remoteEntry_js-CeYV4czE.js} +2 -2
- package/admin/custom/assets/Components-jfkoGyES.js +1 -0
- package/admin/custom/assets/ConfigCustomJavascriptSet__loadShare___mf_0_emotion_mf_1_react__loadShare__.mjs-sg07E3QA.js +1 -0
- package/admin/custom/assets/ConfigCustomJavascriptSet__loadShare___mf_0_emotion_mf_1_styled__loadShare__.mjs-CwjI4A-J.js +1 -0
- package/admin/custom/assets/{ConfigCustomJavascriptSet__loadShare__react__loadShare__.mjs-0SjJESw9.js → ConfigCustomJavascriptSet__loadShare__react__loadShare__.mjs-BW9Qbxx5.js} +1 -1
- package/admin/custom/assets/{ConfigCustomJavascriptSet__loadShare__react__loadShare__.mjs_commonjs-proxy-CInLgMWW.js → ConfigCustomJavascriptSet__loadShare__react__loadShare__.mjs_commonjs-proxy-2W2BYT2K.js} +1 -1
- package/admin/custom/assets/{ConfigCustomJavascriptSet__loadShare__react_mf_2_dom__loadShare__.mjs_commonjs-proxy-CorOEig8.js → ConfigCustomJavascriptSet__loadShare__react_mf_2_dom__loadShare__.mjs_commonjs-proxy-D8zcZsJo.js} +1 -1
- package/admin/custom/assets/DefaultPropsProvider-CfKHPa-G.js +1 -0
- package/admin/custom/assets/{TextField-DNzklBSl.js → TextField-CxPfV5Sr.js} +12 -12
- package/admin/custom/assets/{ThemeProvider-B4G3jidj.js → ThemeProvider-DSTCckLJ.js} +4 -4
- package/admin/custom/assets/{createSvgIcon-ZdrQ8zSU.js → createSvgIcon-b2B2NI3g.js} +1 -1
- package/admin/custom/assets/createTheme-MsdB8zt7.js +8 -0
- package/admin/custom/assets/defaultTheme-CmVP-QNF.js +1 -0
- package/admin/custom/assets/emotion-cache.browser.esm-BgJfNZJN.js +1 -0
- package/admin/custom/assets/emotion-react.browser.esm-chZAeh_-.js +1 -0
- package/admin/custom/assets/emotion-serialize.esm-BUa21YfQ.js +1 -0
- package/admin/custom/assets/emotion-styled.browser.esm-Yi13plpH.js +1 -0
- package/admin/custom/assets/emotion-use-insertion-effect-with-fallbacks.browser.esm-CEPeBbh9.js +1 -0
- package/admin/custom/assets/extends-CF3RwP-h.js +1 -0
- package/admin/custom/assets/index-17vbGv00.js +1 -0
- package/admin/custom/assets/{index-iRHaQfi-.js → index-B5DqCD_a.js} +1 -1
- package/admin/custom/assets/{index-DNKzDki_.js → index-CS_GBN2m.js} +159 -159
- package/admin/custom/assets/{index-NItS-TWp.js → index-ChQotUCl.js} +1 -1
- package/admin/custom/assets/{index-YBvFYuP6.js → index-CzRngQHl.js} +1 -1
- package/admin/custom/assets/index-DLNtxshy.js +1 -0
- package/admin/custom/assets/{index-Dp7A3XHd.js → index-DV76KW3-.js} +4 -4
- package/admin/custom/assets/{index-D0iTiVVG.js → index-DlQbqHom.js} +1 -1
- package/admin/custom/assets/{index-BptSiKBK.js → index-FTmk0JJx.js} +5 -5
- package/admin/custom/assets/index-qs6J40Tp.js +964 -0
- package/admin/custom/assets/{jsx-runtime-DfVU3AZG.js → jsx-runtime-BplTyRFk.js} +1 -1
- package/admin/custom/assets/localSharedImportMap-BeObsVNc.js +1 -0
- package/admin/custom/assets/useTheme-BV4xOYIL.js +1 -0
- package/admin/custom/assets/useTheme-ldICnlmf.js +1 -0
- package/admin/custom/assets/{virtualExposes-jSvr-kK3.js → virtualExposes-wYe_Np-4.js} +1 -1
- package/admin/custom/assets/{virtual_mf-REMOTE_ENTRY_ID_ConfigCustomJavascriptSet__customComponents_js-Br3IlnqO.js → virtual_mf-REMOTE_ENTRY_ID_ConfigCustomJavascriptSet__customComponents_js-BcRVut8U.js} +2 -2
- package/admin/custom/customComponents.js +1 -1
- package/admin/google-blockly/own/field_oid.js +151 -2
- package/admin/mf-manifest.json +1 -1
- package/admin/remoteEntry.js +1 -1
- package/admin/tab.html +10 -8
- package/build/lib/eventObj.js +21 -12
- package/build/lib/eventObj.js.map +1 -1
- package/build/lib/sandbox.js +168 -58
- package/build/lib/sandbox.js.map +1 -1
- package/build/lib/scheduler.js +18 -13
- package/build/lib/scheduler.js.map +1 -1
- package/build/main.js +224 -135
- package/build/main.js.map +1 -1
- package/build/types.d.ts +14 -6
- package/io-package.json +14 -1
- package/package.json +1 -1
- package/admin/assets/ScriptEditorVanillaMonaco-CaFyKuJ2.js +0 -3
- package/admin/assets/index-SHs7_wDh.js +0 -1
- package/admin/assets/stateHoverProvider-CGChzV5W.js +0 -3
- package/admin/custom/assets/Components-B9ZyjeRI.js +0 -1
- package/admin/custom/assets/DefaultPropsProvider-CGb21NGY.js +0 -1
- package/admin/custom/assets/createTheme-Wd4QxQE0.js +0 -8
- package/admin/custom/assets/defaultTheme-F2iJF2G2.js +0 -1
- package/admin/custom/assets/index-AZB1OHDH.js +0 -1
- package/admin/custom/assets/index-C0RmLaAj.js +0 -1
- package/admin/custom/assets/index-sozs10Ij.js +0 -960
- package/admin/custom/assets/localSharedImportMap-BxT0VwTO.js +0 -1
- package/admin/custom/assets/useTheme-BfhDdtAr.js +0 -1
- package/admin/custom/assets/useTheme-DWZ6jNyq.js +0 -1
- /package/admin/custom/assets/{index-huvZQhZw.js → index-D_5kZmfw.js} +0 -0
- /package/admin/custom/assets/{index-CmJ8HzSc.js → index-DpJ18x6R.js} +0 -0
- /package/admin/custom/assets/{leaflet-src-k2rCV-Ix.js → leaflet-src-B472P7Y1.js} +0 -0
package/build/main.js
CHANGED
|
@@ -227,18 +227,32 @@ const jsDeclarationServer = new virtual_tsc_1.Server(typescriptSettings_1.jsDecl
|
|
|
227
227
|
* Stores the IDs of script objects whose change should be ignored because
|
|
228
228
|
* the compiled source was just updated
|
|
229
229
|
*/
|
|
230
|
+
const HTTP_STATUS_TEXTS = {
|
|
231
|
+
400: 'Bad Request',
|
|
232
|
+
401: 'Unauthorized',
|
|
233
|
+
403: 'Forbidden',
|
|
234
|
+
404: 'Not Found',
|
|
235
|
+
429: 'Too Many Requests / Rate Limit',
|
|
236
|
+
500: 'Internal Server Error',
|
|
237
|
+
502: 'Bad Gateway',
|
|
238
|
+
503: 'Service Unavailable',
|
|
239
|
+
};
|
|
230
240
|
function httpStatusText(code) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
241
|
+
return HTTP_STATUS_TEXTS[code] ?? `Error ${code}`;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Resolves the correct http/https module based on the URL string.
|
|
245
|
+
* Returns null if the URL is invalid.
|
|
246
|
+
*/
|
|
247
|
+
function resolveRequestModule(url) {
|
|
248
|
+
try {
|
|
249
|
+
const { protocol } = new URL(url);
|
|
250
|
+
const isHttps = protocol === 'https:';
|
|
251
|
+
return { module: isHttps ? https : http, isHttps };
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
242
256
|
}
|
|
243
257
|
class JavaScript extends adapter_core_1.Adapter {
|
|
244
258
|
context;
|
|
@@ -256,15 +270,26 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
256
270
|
states = {};
|
|
257
271
|
interimStateValues = {};
|
|
258
272
|
stateIds = [];
|
|
273
|
+
/** Fast O(1) lookup set – always kept in sync with stateIds */
|
|
274
|
+
stateIdSet = new Set();
|
|
259
275
|
subscriptions = [];
|
|
260
276
|
subscriptionsFile = [];
|
|
261
277
|
subscriptionsObject = [];
|
|
278
|
+
/** O(1) dispatch map for subscriptionsObject – pattern → subscribers */
|
|
279
|
+
subscriptionsObjectMap = new Map();
|
|
280
|
+
/** IO-9: Cache for sendTo broadcast – adapterName → instance list, invalidated on object change */
|
|
281
|
+
sendToInstanceCache = new Map();
|
|
262
282
|
subscribedPatterns = {};
|
|
263
283
|
subscribedPatternsFile = {};
|
|
264
284
|
adapterSubs = {};
|
|
265
285
|
timers = {};
|
|
266
|
-
|
|
286
|
+
/** Reverse-index: scriptName → Set of stateIds that have timers for this script – O(1) cleanup */
|
|
287
|
+
timersByScript = new Map();
|
|
288
|
+
/** O(1) Set for enum-id lookups – replaces sorted string[] array */
|
|
289
|
+
_enums = new Set();
|
|
267
290
|
names = {}; // name: id
|
|
291
|
+
/** Reverse map: id → name for O(1) getName() lookups */
|
|
292
|
+
nameById = new Map();
|
|
268
293
|
scripts = {};
|
|
269
294
|
password = '';
|
|
270
295
|
messageBusHandlers = {};
|
|
@@ -395,11 +420,14 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
395
420
|
subscriptions: this.subscriptions,
|
|
396
421
|
subscriptionsFile: this.subscriptionsFile,
|
|
397
422
|
subscriptionsObject: this.subscriptionsObject,
|
|
423
|
+
subscriptionsObjectMap: this.subscriptionsObjectMap,
|
|
424
|
+
sendToInstanceCache: this.sendToInstanceCache,
|
|
398
425
|
subscribedPatterns: this.subscribedPatterns,
|
|
399
426
|
subscribedPatternsFile: this.subscribedPatternsFile,
|
|
400
427
|
adapterSubs: this.adapterSubs,
|
|
401
428
|
cacheObjectEnums: {},
|
|
402
429
|
timers: this.timers,
|
|
430
|
+
timersByScript: this.timersByScript,
|
|
403
431
|
enums: this._enums,
|
|
404
432
|
names: this.names,
|
|
405
433
|
scripts: this.scripts,
|
|
@@ -446,20 +474,20 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
446
474
|
if (id.startsWith('enum.')) {
|
|
447
475
|
// clear cache
|
|
448
476
|
this.context.cacheObjectEnums = {};
|
|
449
|
-
// update this._enums
|
|
477
|
+
// update this._enums Set
|
|
450
478
|
if (obj) {
|
|
451
|
-
|
|
452
|
-
if (!this._enums.includes(id)) {
|
|
453
|
-
this._enums.push(id);
|
|
454
|
-
this._enums.sort();
|
|
455
|
-
}
|
|
479
|
+
this._enums.add(id);
|
|
456
480
|
}
|
|
457
481
|
else {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
482
|
+
this._enums.delete(id);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
// IO-9: Invalidate sendTo instance-cache when adapter instances change
|
|
486
|
+
if (id.startsWith('system.adapter.')) {
|
|
487
|
+
const parts = id.split('.');
|
|
488
|
+
if (parts.length >= 3) {
|
|
489
|
+
const adapterName = parts[2]; // e.g. "zigbee" from "system.adapter.zigbee.0"
|
|
490
|
+
this.sendToInstanceCache.delete(adapterName);
|
|
463
491
|
}
|
|
464
492
|
}
|
|
465
493
|
if (id === 'system.config' && obj?.common?.language) {
|
|
@@ -494,9 +522,10 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
494
522
|
/* ignore */
|
|
495
523
|
});
|
|
496
524
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
525
|
+
// O(1) dispatch via pattern map instead of O(n) forEach
|
|
526
|
+
const objSubs = this.subscriptionsObjectMap.get(id);
|
|
527
|
+
if (objSubs) {
|
|
528
|
+
for (const sub of objSubs) {
|
|
500
529
|
try {
|
|
501
530
|
sub.callback(id, obj);
|
|
502
531
|
}
|
|
@@ -504,7 +533,7 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
504
533
|
this.log.error(`Error in callback: ${err.toString()}`);
|
|
505
534
|
}
|
|
506
535
|
}
|
|
507
|
-
}
|
|
536
|
+
}
|
|
508
537
|
// handle Script object updates
|
|
509
538
|
if (!obj && formerObj?.type === 'script') {
|
|
510
539
|
// Object Deleted just now
|
|
@@ -631,16 +660,16 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
631
660
|
if (this.adapterSubs[id]) {
|
|
632
661
|
const parts = id.split('.');
|
|
633
662
|
const a = `${parts[2]}.${parts[3]}`;
|
|
634
|
-
for (
|
|
635
|
-
this.log.info(`Detected coming adapter "${a}". Send subscribe: ${
|
|
636
|
-
this.sendTo(a, 'subscribe',
|
|
663
|
+
for (const sub of this.adapterSubs[id]) {
|
|
664
|
+
this.log.info(`Detected coming adapter "${a}". Send subscribe: ${sub}`);
|
|
665
|
+
this.sendTo(a, 'subscribe', sub);
|
|
637
666
|
}
|
|
638
667
|
}
|
|
639
668
|
}
|
|
640
669
|
}
|
|
641
|
-
else if ( /*!oldState && */!this.
|
|
642
|
-
this.
|
|
643
|
-
this.
|
|
670
|
+
else if ( /*!oldState && */!this.stateIdSet.has(id)) {
|
|
671
|
+
this.sortedInsert(id);
|
|
672
|
+
this.stateIdSet.add(id);
|
|
644
673
|
}
|
|
645
674
|
this.states[id] = state;
|
|
646
675
|
}
|
|
@@ -649,9 +678,10 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
649
678
|
delete this.states[id];
|
|
650
679
|
}
|
|
651
680
|
state = {};
|
|
652
|
-
const pos = this.stateIds
|
|
681
|
+
const pos = this.binaryIndexOf(this.stateIds, id);
|
|
653
682
|
if (pos !== -1) {
|
|
654
683
|
this.stateIds.splice(pos, 1);
|
|
684
|
+
this.stateIdSet.delete(id);
|
|
655
685
|
}
|
|
656
686
|
}
|
|
657
687
|
const _eventObj = (0, eventObj_1.createEventObject)(this.context, id, this.convertBackStringifiedValues(id, state), this.convertBackStringifiedValues(id, oldState));
|
|
@@ -683,15 +713,22 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
683
713
|
}
|
|
684
714
|
}
|
|
685
715
|
async onUnload(callback) {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
716
|
+
try {
|
|
717
|
+
await this.debugStop();
|
|
718
|
+
this.stopTimeSchedules();
|
|
719
|
+
if (this.setStateCountCheckInterval) {
|
|
720
|
+
clearInterval(this.setStateCountCheckInterval);
|
|
721
|
+
this.setStateCountCheckInterval = null;
|
|
722
|
+
}
|
|
723
|
+
await this.stopAllScripts();
|
|
691
724
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
725
|
+
catch (err) {
|
|
726
|
+
this.log.error(`Error during unload: ${err.message}`);
|
|
727
|
+
}
|
|
728
|
+
finally {
|
|
729
|
+
if (typeof callback === 'function') {
|
|
730
|
+
callback();
|
|
731
|
+
}
|
|
695
732
|
}
|
|
696
733
|
}
|
|
697
734
|
async onReady() {
|
|
@@ -989,16 +1026,12 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
989
1026
|
const body = JSON.stringify(bodyObj);
|
|
990
1027
|
const bodyBuffer = Buffer.from(body, 'utf8');
|
|
991
1028
|
chatHeaders['Content-Length'] = bodyBuffer.length;
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
urlObj = new URL(url);
|
|
995
|
-
}
|
|
996
|
-
catch {
|
|
1029
|
+
const resolved = resolveRequestModule(url);
|
|
1030
|
+
if (!resolved) {
|
|
997
1031
|
this.sendTo(obj.from, obj.command, { error: `Invalid API URL: ${url}` }, obj.callback);
|
|
998
1032
|
break;
|
|
999
1033
|
}
|
|
1000
|
-
const isHttps =
|
|
1001
|
-
const requestModule = isHttps ? https : http;
|
|
1034
|
+
const { module: requestModule, isHttps } = resolved;
|
|
1002
1035
|
try {
|
|
1003
1036
|
const req = requestModule.request(url, {
|
|
1004
1037
|
method: 'POST',
|
|
@@ -1110,16 +1143,12 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1110
1143
|
testHeaders.Authorization = `Bearer ${apiKey}`;
|
|
1111
1144
|
}
|
|
1112
1145
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
urlObj = new URL(url);
|
|
1116
|
-
}
|
|
1117
|
-
catch {
|
|
1146
|
+
const resolved = resolveRequestModule(url);
|
|
1147
|
+
if (!resolved) {
|
|
1118
1148
|
this.sendTo(obj.from, obj.command, { error: `Invalid API URL: ${url}` }, obj.callback);
|
|
1119
1149
|
break;
|
|
1120
1150
|
}
|
|
1121
|
-
const isHttps =
|
|
1122
|
-
const requestModule = isHttps ? https : http;
|
|
1151
|
+
const { module: requestModule, isHttps } = resolved;
|
|
1123
1152
|
try {
|
|
1124
1153
|
const req = requestModule.request(url, {
|
|
1125
1154
|
method: 'GET',
|
|
@@ -1234,14 +1263,16 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1234
1263
|
}
|
|
1235
1264
|
}
|
|
1236
1265
|
onLog(msg) {
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
(handler.
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1266
|
+
for (const name in this.logSubscriptions) {
|
|
1267
|
+
for (const handler of this.logSubscriptions[name]) {
|
|
1268
|
+
if (typeof handler.cb === 'function' &&
|
|
1269
|
+
(handler.severity === '*' || handler.severity === msg.severity)) {
|
|
1270
|
+
handler.sandbox.logHandler = handler.severity || '*';
|
|
1271
|
+
handler.cb.call(handler.sandbox, msg);
|
|
1272
|
+
handler.sandbox.logHandler = undefined;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1245
1276
|
}
|
|
1246
1277
|
logError(scriptName, msg, e, offs) {
|
|
1247
1278
|
const stack = e.stack ? e.stack.toString().split('\n') : e ? e.toString() : '';
|
|
@@ -1495,9 +1526,9 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1495
1526
|
}
|
|
1496
1527
|
// CHeck setState counter per minute and stop a script if too high
|
|
1497
1528
|
this.setStateCountCheckInterval = setInterval(() => {
|
|
1498
|
-
|
|
1529
|
+
for (const id in this.scripts) {
|
|
1499
1530
|
if (!this.scripts[id]) {
|
|
1500
|
-
|
|
1531
|
+
continue;
|
|
1501
1532
|
}
|
|
1502
1533
|
const currentSetStatePerMinuteCounter = this.scripts[id].setStatePerMinuteCounter;
|
|
1503
1534
|
this.scripts[id].setStatePerMinuteCounter = 0;
|
|
@@ -1515,7 +1546,7 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1515
1546
|
this.scripts[id].setStatePerMinuteProblemCounter--;
|
|
1516
1547
|
this.log.debug(`Script ${id} has NOT reached the maximum of ${this.config.maxSetStatePerMinute} setState calls per minute. Decrease problem counter to ${this.scripts[id].setStatePerMinuteProblemCounter}`);
|
|
1517
1548
|
}
|
|
1518
|
-
}
|
|
1549
|
+
}
|
|
1519
1550
|
}, 60000);
|
|
1520
1551
|
}
|
|
1521
1552
|
loadTypeScriptDeclarations() {
|
|
@@ -1534,10 +1565,15 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1534
1565
|
.split(/[,;\s]+/)
|
|
1535
1566
|
.map(s => s.trim())
|
|
1536
1567
|
.filter(s => !!s);
|
|
1568
|
+
// O(1) lookups – avoids O(n²) Array.includes inside loops
|
|
1569
|
+
const installedSet = new Set(installedLibs);
|
|
1570
|
+
const wantsSet = new Set(wantsTypings);
|
|
1571
|
+
const packagesSet = new Set(packages);
|
|
1537
1572
|
// Add all installed libraries the user has requested typings for to the list of packages
|
|
1538
1573
|
for (const lib of installedLibs) {
|
|
1539
|
-
if (
|
|
1574
|
+
if (wantsSet.has(lib) && !packagesSet.has(lib)) {
|
|
1540
1575
|
packages.push(lib);
|
|
1576
|
+
packagesSet.add(lib);
|
|
1541
1577
|
}
|
|
1542
1578
|
}
|
|
1543
1579
|
// Some packages have submodules (e.g., rxjs/operators) that are not exposed through the main entry point
|
|
@@ -1548,8 +1584,9 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1548
1584
|
continue;
|
|
1549
1585
|
}
|
|
1550
1586
|
const pkgName = lib.substring(0, lib.indexOf('/'));
|
|
1551
|
-
if (
|
|
1587
|
+
if (installedSet.has(pkgName) && !packagesSet.has(lib)) {
|
|
1552
1588
|
packages.push(lib);
|
|
1589
|
+
packagesSet.add(lib);
|
|
1553
1590
|
}
|
|
1554
1591
|
}
|
|
1555
1592
|
}
|
|
@@ -1583,43 +1620,38 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1583
1620
|
if (obj) {
|
|
1584
1621
|
// add state to state ID's list
|
|
1585
1622
|
if (obj.type === 'state') {
|
|
1586
|
-
if (!this.
|
|
1587
|
-
this.
|
|
1588
|
-
this.
|
|
1623
|
+
if (!this.stateIdSet.has(id)) {
|
|
1624
|
+
this.sortedInsert(id);
|
|
1625
|
+
this.stateIdSet.add(id);
|
|
1589
1626
|
}
|
|
1590
1627
|
if (this.context.devices && this.context.channels) {
|
|
1591
1628
|
const parts = id.split('.');
|
|
1592
1629
|
parts.pop();
|
|
1593
1630
|
const chn = parts.join('.');
|
|
1594
|
-
this.context.channels[chn] ||=
|
|
1595
|
-
this.context.channels[chn].
|
|
1631
|
+
this.context.channels[chn] ||= new Set();
|
|
1632
|
+
this.context.channels[chn].add(id);
|
|
1596
1633
|
parts.pop();
|
|
1597
1634
|
const dev = parts.join('.');
|
|
1598
|
-
this.context.devices[dev] ||=
|
|
1599
|
-
this.context.devices[dev].
|
|
1635
|
+
this.context.devices[dev] ||= new Set();
|
|
1636
|
+
this.context.devices[dev].add(id);
|
|
1600
1637
|
}
|
|
1601
1638
|
}
|
|
1602
1639
|
}
|
|
1603
1640
|
else {
|
|
1604
1641
|
// delete object from state ID's list
|
|
1605
|
-
const pos = this.stateIds
|
|
1642
|
+
const pos = this.binaryIndexOf(this.stateIds, id);
|
|
1606
1643
|
if (pos !== -1) {
|
|
1607
1644
|
this.stateIds.splice(pos, 1);
|
|
1645
|
+
this.stateIdSet.delete(id);
|
|
1608
1646
|
}
|
|
1609
1647
|
if (this.context.devices && this.context.channels) {
|
|
1610
1648
|
const parts = id.split('.');
|
|
1611
1649
|
parts.pop();
|
|
1612
1650
|
const chn = parts.join('.');
|
|
1613
|
-
|
|
1614
|
-
const posChn = this.context.channels[chn].indexOf(id);
|
|
1615
|
-
posChn !== -1 && this.context.channels[chn].splice(posChn, 1);
|
|
1616
|
-
}
|
|
1651
|
+
this.context.channels[chn]?.delete(id);
|
|
1617
1652
|
parts.pop();
|
|
1618
1653
|
const dev = parts.join('.');
|
|
1619
|
-
|
|
1620
|
-
const posDev = this.context.devices[dev].indexOf(id);
|
|
1621
|
-
posDev !== -1 && this.context.devices[dev].splice(posDev, 1);
|
|
1622
|
-
}
|
|
1654
|
+
this.context.devices[dev]?.delete(id);
|
|
1623
1655
|
}
|
|
1624
1656
|
delete this.folderCreationVerifiedObjects[id];
|
|
1625
1657
|
}
|
|
@@ -1744,11 +1776,12 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1744
1776
|
this.context.states = this.states;
|
|
1745
1777
|
this.addGetProperty(this.states);
|
|
1746
1778
|
}
|
|
1747
|
-
// remember all IDs
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1779
|
+
// remember all IDs – sort once to guarantee the sorted invariant
|
|
1780
|
+
// required by binaryIndexOf() / sortedInsert() used later
|
|
1781
|
+
const keys = Object.keys(res).sort();
|
|
1782
|
+
for (const id of keys) {
|
|
1783
|
+
this.stateIds.push(id);
|
|
1784
|
+
this.stateIdSet.add(id);
|
|
1752
1785
|
}
|
|
1753
1786
|
this.statesInitDone = true;
|
|
1754
1787
|
this.log.info('received all states');
|
|
@@ -1768,17 +1801,18 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1768
1801
|
this.objects = {};
|
|
1769
1802
|
this.context.objects = this.objects;
|
|
1770
1803
|
for (let i = 0; i < res.rows.length; i++) {
|
|
1771
|
-
|
|
1804
|
+
const doc = res.rows[i]?.doc;
|
|
1805
|
+
if (!doc) {
|
|
1772
1806
|
this.log.debug(`Got empty object for index ${i} (${res.rows[i].id})`);
|
|
1773
1807
|
continue;
|
|
1774
1808
|
}
|
|
1775
|
-
if (this.objects[
|
|
1809
|
+
if (this.objects[doc._id] === undefined) {
|
|
1776
1810
|
// If was already there, ignore
|
|
1777
|
-
this.objects[
|
|
1811
|
+
this.objects[doc._id] = doc;
|
|
1778
1812
|
}
|
|
1779
|
-
|
|
1813
|
+
doc.type === 'enum' && this._enums.add(doc._id);
|
|
1780
1814
|
// Collect all names
|
|
1781
|
-
this.addToNames(this.objects[
|
|
1815
|
+
this.addToNames(this.objects[doc._id]);
|
|
1782
1816
|
}
|
|
1783
1817
|
this.addGetProperty(this.objects);
|
|
1784
1818
|
const systemConfig = this.objects['system.config'];
|
|
@@ -1933,37 +1967,32 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1933
1967
|
}
|
|
1934
1968
|
this.names[name].push(id);
|
|
1935
1969
|
}
|
|
1970
|
+
// keep reverse-map up to date for O(1) getName()
|
|
1971
|
+
this.nameById.set(id, name);
|
|
1936
1972
|
}
|
|
1937
1973
|
}
|
|
1938
1974
|
removeFromNames(id) {
|
|
1939
1975
|
const n = this.getName(id);
|
|
1940
1976
|
if (n) {
|
|
1941
1977
|
if (Array.isArray(this.names[n])) {
|
|
1942
|
-
const
|
|
1978
|
+
const arr = this.names[n];
|
|
1979
|
+
const pos = arr.indexOf(id);
|
|
1943
1980
|
if (pos > -1) {
|
|
1944
|
-
|
|
1945
|
-
if (
|
|
1946
|
-
this.names[n] =
|
|
1981
|
+
arr.splice(pos, 1);
|
|
1982
|
+
if (arr.length === 1) {
|
|
1983
|
+
this.names[n] = arr[0];
|
|
1947
1984
|
}
|
|
1948
1985
|
}
|
|
1949
1986
|
}
|
|
1950
1987
|
else {
|
|
1951
1988
|
delete this.names[n];
|
|
1952
1989
|
}
|
|
1990
|
+
// keep reverse-map up to date for O(1) getName()
|
|
1991
|
+
this.nameById.delete(id);
|
|
1953
1992
|
}
|
|
1954
1993
|
}
|
|
1955
1994
|
getName(id) {
|
|
1956
|
-
|
|
1957
|
-
if (this.names[n] && Array.isArray(this.names[n])) {
|
|
1958
|
-
if (this.names[n].includes(id)) {
|
|
1959
|
-
return n;
|
|
1960
|
-
}
|
|
1961
|
-
}
|
|
1962
|
-
else if (this.names[n] === id) {
|
|
1963
|
-
return n;
|
|
1964
|
-
}
|
|
1965
|
-
}
|
|
1966
|
-
return null;
|
|
1995
|
+
return this.nameById.get(id) ?? null;
|
|
1967
1996
|
}
|
|
1968
1997
|
async installNpm(npmLib) {
|
|
1969
1998
|
return new Promise((resolve, reject) => {
|
|
@@ -1977,6 +2006,7 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1977
2006
|
const child = this.mods.child_process.exec(cmd, {
|
|
1978
2007
|
windowsHide: true,
|
|
1979
2008
|
cwd: path,
|
|
2009
|
+
timeout: 120_000, // 2 minutes max – prevents infinite blocking
|
|
1980
2010
|
});
|
|
1981
2011
|
child.stdout?.on('data', buf => this.log.info(buf.toString('utf8')));
|
|
1982
2012
|
child.stderr?.on('data', buf => this.log.error(buf.toString('utf8')));
|
|
@@ -2124,8 +2154,8 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
2124
2154
|
}
|
|
2125
2155
|
}
|
|
2126
2156
|
execute(script, name, engineType, verbose, debug) {
|
|
2127
|
-
script.intervals =
|
|
2128
|
-
script.timeouts =
|
|
2157
|
+
script.intervals = new Set();
|
|
2158
|
+
script.timeouts = new Set();
|
|
2129
2159
|
script.schedules = [];
|
|
2130
2160
|
script.wizards = [];
|
|
2131
2161
|
script.name = name;
|
|
@@ -2157,13 +2187,54 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
2157
2187
|
this.logError(name, 'Error by run:', err);
|
|
2158
2188
|
}
|
|
2159
2189
|
}
|
|
2190
|
+
/**
|
|
2191
|
+
* Finds the index of `id` in a sorted array using binary search – O(log n).
|
|
2192
|
+
* Returns -1 if not found. Used instead of Array.indexOf on stateIds.
|
|
2193
|
+
*/
|
|
2194
|
+
binaryIndexOf(arr, id) {
|
|
2195
|
+
let lo = 0;
|
|
2196
|
+
let hi = arr.length - 1;
|
|
2197
|
+
while (lo <= hi) {
|
|
2198
|
+
const mid = (lo + hi) >>> 1;
|
|
2199
|
+
if (arr[mid] === id) {
|
|
2200
|
+
return mid;
|
|
2201
|
+
}
|
|
2202
|
+
else if (arr[mid] < id) {
|
|
2203
|
+
lo = mid + 1;
|
|
2204
|
+
}
|
|
2205
|
+
else {
|
|
2206
|
+
hi = mid - 1;
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
return -1;
|
|
2210
|
+
}
|
|
2211
|
+
/**
|
|
2212
|
+
* Inserts `id` into the sorted `stateIds` array using binary search – O(log n).
|
|
2213
|
+
* Much faster than push() + sort() which is O(n log n) on every insertion.
|
|
2214
|
+
*/
|
|
2215
|
+
sortedInsert(id) {
|
|
2216
|
+
let lo = 0;
|
|
2217
|
+
let hi = this.stateIds.length;
|
|
2218
|
+
while (lo < hi) {
|
|
2219
|
+
const mid = (lo + hi) >>> 1;
|
|
2220
|
+
if (this.stateIds[mid] < id) {
|
|
2221
|
+
lo = mid + 1;
|
|
2222
|
+
}
|
|
2223
|
+
else {
|
|
2224
|
+
hi = mid;
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
if (this.stateIds[lo] !== id) {
|
|
2228
|
+
this.stateIds.splice(lo, 0, id);
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2160
2231
|
unsubscribe(id) {
|
|
2161
2232
|
if (!id) {
|
|
2162
2233
|
this.log.warn('unsubscribe: empty name');
|
|
2163
2234
|
return;
|
|
2164
2235
|
}
|
|
2165
2236
|
if (Array.isArray(id)) {
|
|
2166
|
-
id.forEach(sub => unsubscribe(sub));
|
|
2237
|
+
id.forEach(sub => this.unsubscribe(sub));
|
|
2167
2238
|
return;
|
|
2168
2239
|
}
|
|
2169
2240
|
if (id.constructor && id.constructor.name === 'RegExp') {
|
|
@@ -2180,11 +2251,8 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
2180
2251
|
const a = `${parts[0]}.${parts[1]}`;
|
|
2181
2252
|
const alive = `system.adapter.${a}.alive`;
|
|
2182
2253
|
if (this.adapterSubs[alive]) {
|
|
2183
|
-
|
|
2184
|
-
if (
|
|
2185
|
-
this.adapterSubs[alive].splice(pos, 1);
|
|
2186
|
-
}
|
|
2187
|
-
if (!this.adapterSubs[alive].length) {
|
|
2254
|
+
this.adapterSubs[alive].delete(id);
|
|
2255
|
+
if (!this.adapterSubs[alive].size) {
|
|
2188
2256
|
delete this.adapterSubs[alive];
|
|
2189
2257
|
}
|
|
2190
2258
|
}
|
|
@@ -2291,31 +2359,46 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
2291
2359
|
if (this.subscriptionsObject[i].name === name) {
|
|
2292
2360
|
const sub = this.subscriptionsObject.splice(i, 1)[0];
|
|
2293
2361
|
if (sub) {
|
|
2362
|
+
// Remove from O(1) dispatch map
|
|
2363
|
+
const mapSubs = this.subscriptionsObjectMap.get(sub.pattern);
|
|
2364
|
+
if (mapSubs) {
|
|
2365
|
+
const pos = mapSubs.indexOf(sub);
|
|
2366
|
+
if (pos !== -1) {
|
|
2367
|
+
mapSubs.splice(pos, 1);
|
|
2368
|
+
}
|
|
2369
|
+
if (!mapSubs.length) {
|
|
2370
|
+
this.subscriptionsObjectMap.delete(sub.pattern);
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2294
2373
|
this.unsubscribeForeignObjects(sub.pattern);
|
|
2295
2374
|
}
|
|
2296
2375
|
}
|
|
2297
2376
|
}
|
|
2298
2377
|
// Stop all timeouts
|
|
2299
|
-
for (
|
|
2300
|
-
clearTimeout(
|
|
2378
|
+
for (const t of this.scripts[name].timeouts) {
|
|
2379
|
+
clearTimeout(t);
|
|
2301
2380
|
}
|
|
2302
2381
|
// Stop all intervals
|
|
2303
|
-
for (
|
|
2304
|
-
clearInterval(
|
|
2305
|
-
}
|
|
2306
|
-
// Stop all delayed states (setStateDelayed timers)
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
this.timers[stateId].
|
|
2382
|
+
for (const t of this.scripts[name].intervals) {
|
|
2383
|
+
clearInterval(t);
|
|
2384
|
+
}
|
|
2385
|
+
// Stop all delayed states (setStateDelayed timers) – O(1) via reverse-index
|
|
2386
|
+
const scriptStateIds = this.timersByScript.get(name);
|
|
2387
|
+
if (scriptStateIds) {
|
|
2388
|
+
for (const stateId of scriptStateIds) {
|
|
2389
|
+
if (this.timers[stateId]) {
|
|
2390
|
+
for (let i = this.timers[stateId].length - 1; i >= 0; i--) {
|
|
2391
|
+
if (this.timers[stateId][i].scriptName === name) {
|
|
2392
|
+
clearTimeout(this.timers[stateId][i].t);
|
|
2393
|
+
this.timers[stateId].splice(i, 1);
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
if (!this.timers[stateId].length) {
|
|
2397
|
+
delete this.timers[stateId];
|
|
2313
2398
|
}
|
|
2314
|
-
}
|
|
2315
|
-
if (!this.timers[stateId].length) {
|
|
2316
|
-
delete this.timers[stateId];
|
|
2317
2399
|
}
|
|
2318
2400
|
}
|
|
2401
|
+
this.timersByScript.delete(name);
|
|
2319
2402
|
}
|
|
2320
2403
|
// Stop all scheduled jobs
|
|
2321
2404
|
for (let i = 0; i < this.scripts[name].schedules.length; i++) {
|
|
@@ -2562,6 +2645,11 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
2562
2645
|
this.timeScheduleTimer = setTimeout(() => this.timeSchedule(), interval);
|
|
2563
2646
|
}
|
|
2564
2647
|
async dayTimeSchedules() {
|
|
2648
|
+
// Always clear any existing timer to prevent memory leaks on rapid re-scheduling
|
|
2649
|
+
if (this.dayScheduleTimer) {
|
|
2650
|
+
clearTimeout(this.dayScheduleTimer);
|
|
2651
|
+
this.dayScheduleTimer = null;
|
|
2652
|
+
}
|
|
2565
2653
|
// get astrological event
|
|
2566
2654
|
if (this.config.latitude === undefined ||
|
|
2567
2655
|
this.config.longitude === undefined ||
|
|
@@ -2958,14 +3046,15 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
2958
3046
|
}
|
|
2959
3047
|
function patternMatching(event, patternFunctions) {
|
|
2960
3048
|
let matched = false;
|
|
3049
|
+
const logic = patternFunctions.logic;
|
|
2961
3050
|
for (let i = 0, len = patternFunctions.length; i < len; i++) {
|
|
2962
3051
|
if (patternFunctions[i](event)) {
|
|
2963
|
-
if (
|
|
3052
|
+
if (logic === 'or') {
|
|
2964
3053
|
return true;
|
|
2965
3054
|
}
|
|
2966
3055
|
matched = true;
|
|
2967
3056
|
}
|
|
2968
|
-
else if (
|
|
3057
|
+
else if (logic === 'and') {
|
|
2969
3058
|
return false;
|
|
2970
3059
|
}
|
|
2971
3060
|
}
|