iobroker.javascript 9.2.2 → 9.2.4
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 +10 -25
- package/admin/assets/{AiChatPanel-Bbux7LP7.js → AiChatPanel-B4vPXgLz.js} +1 -1
- package/admin/assets/{ScriptEditor-DKljeYQN.js → ScriptEditor-CmEeMfC2.js} +1 -1
- package/admin/assets/ScriptEditorVanillaMonaco-C22Gpet7.js +3 -0
- package/admin/assets/cronHoverProvider-Bg0Vz-1E.js +2 -0
- package/admin/assets/index-45IwpC7_.js +1 -0
- package/admin/assets/{index-DgE6XhUM.js → index-7mrTpzQy.js} +1 -1
- package/admin/assets/{index-DSvAO584.js → index-BTQOdA7v.js} +83 -83
- package/admin/assets/{index-_BUuZlI0.js → index-Dggem6AX.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-DxTNxyYN.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-CGiJE5Zh.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-B6NrrORQ.js} +159 -159
- package/admin/custom/assets/index-CT5PwRfY.js +964 -0
- 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-NItS-TWp.js → index-DTbSiEk2.js} +1 -1
- 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/{jsx-runtime-DfVU3AZG.js → jsx-runtime-BplTyRFk.js} +1 -1
- package/admin/custom/assets/localSharedImportMap-ChjFDKsq.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-D402kVGY.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-ClQrb2n1.js} +2 -2
- package/admin/custom/customComponents.js +1 -1
- package/admin/google-blockly/own/field_oid.js +151 -2
- package/admin/i18n/es.json +77 -77
- package/admin/mf-manifest.json +1 -1
- package/admin/remoteEntry.js +1 -1
- package/admin/tab.html +10 -8
- package/build/lib/debugger.js +1 -1
- package/build/lib/debugger.js.map +1 -1
- package/build/lib/eventObj.js +31 -14
- package/build/lib/eventObj.js.map +1 -1
- package/build/lib/inspect.js +3 -3
- package/build/lib/inspect.js.map +1 -1
- package/build/lib/sandbox.js +336 -119
- 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 +282 -149
- package/build/main.js.map +1 -1
- package/build/types.d.ts +18 -6
- package/io-package.json +27 -14
- package/package.json +11 -11
- 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,36 @@ 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 = [];
|
|
276
|
+
/**
|
|
277
|
+
* O(1) dispatch map for subscriptions with exact (non-wildcard) string IDs.
|
|
278
|
+
* Always kept in sync with `subscriptions`.
|
|
279
|
+
*/
|
|
280
|
+
subscriptionsMap = new Map();
|
|
281
|
+
/**
|
|
282
|
+
* Subscriptions whose pattern.id is a RegExp, contains wildcards (*,?), or is undefined.
|
|
283
|
+
* These must still be checked linearly on every state change.
|
|
284
|
+
*/
|
|
285
|
+
subscriptionsWildcard = [];
|
|
260
286
|
subscriptionsFile = [];
|
|
261
287
|
subscriptionsObject = [];
|
|
288
|
+
/** O(1) dispatch map for subscriptionsObject – pattern → subscribers */
|
|
289
|
+
subscriptionsObjectMap = new Map();
|
|
290
|
+
/** IO-9: Cache for sendTo broadcast – adapterName → instance list, invalidated on object change */
|
|
291
|
+
sendToInstanceCache = new Map();
|
|
262
292
|
subscribedPatterns = {};
|
|
263
293
|
subscribedPatternsFile = {};
|
|
264
294
|
adapterSubs = {};
|
|
265
295
|
timers = {};
|
|
266
|
-
|
|
296
|
+
/** Reverse-index: scriptName → Set of stateIds that have timers for this script – O(1) cleanup */
|
|
297
|
+
timersByScript = new Map();
|
|
298
|
+
/** O(1) Set for enum-id lookups – replaces sorted string[] array */
|
|
299
|
+
_enums = new Set();
|
|
267
300
|
names = {}; // name: id
|
|
301
|
+
/** Reverse map: id → name for O(1) getName() lookups */
|
|
302
|
+
nameById = new Map();
|
|
268
303
|
scripts = {};
|
|
269
304
|
password = '';
|
|
270
305
|
messageBusHandlers = {};
|
|
@@ -393,13 +428,18 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
393
428
|
stateIds: this.stateIds,
|
|
394
429
|
errorLogFunction: this.errorLogFunction,
|
|
395
430
|
subscriptions: this.subscriptions,
|
|
431
|
+
subscriptionsMap: this.subscriptionsMap,
|
|
432
|
+
subscriptionsWildcard: this.subscriptionsWildcard,
|
|
396
433
|
subscriptionsFile: this.subscriptionsFile,
|
|
397
434
|
subscriptionsObject: this.subscriptionsObject,
|
|
435
|
+
subscriptionsObjectMap: this.subscriptionsObjectMap,
|
|
436
|
+
sendToInstanceCache: this.sendToInstanceCache,
|
|
398
437
|
subscribedPatterns: this.subscribedPatterns,
|
|
399
438
|
subscribedPatternsFile: this.subscribedPatternsFile,
|
|
400
439
|
adapterSubs: this.adapterSubs,
|
|
401
440
|
cacheObjectEnums: {},
|
|
402
441
|
timers: this.timers,
|
|
442
|
+
timersByScript: this.timersByScript,
|
|
403
443
|
enums: this._enums,
|
|
404
444
|
names: this.names,
|
|
405
445
|
scripts: this.scripts,
|
|
@@ -446,20 +486,20 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
446
486
|
if (id.startsWith('enum.')) {
|
|
447
487
|
// clear cache
|
|
448
488
|
this.context.cacheObjectEnums = {};
|
|
449
|
-
// update this._enums
|
|
489
|
+
// update this._enums Set
|
|
450
490
|
if (obj) {
|
|
451
|
-
|
|
452
|
-
if (!this._enums.includes(id)) {
|
|
453
|
-
this._enums.push(id);
|
|
454
|
-
this._enums.sort();
|
|
455
|
-
}
|
|
491
|
+
this._enums.add(id);
|
|
456
492
|
}
|
|
457
493
|
else {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
494
|
+
this._enums.delete(id);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
// IO-9: Invalidate sendTo instance-cache when adapter instances change
|
|
498
|
+
if (id.startsWith('system.adapter.')) {
|
|
499
|
+
const parts = id.split('.');
|
|
500
|
+
if (parts.length >= 3) {
|
|
501
|
+
const adapterName = parts[2]; // e.g. "zigbee" from "system.adapter.zigbee.0"
|
|
502
|
+
this.sendToInstanceCache.delete(adapterName);
|
|
463
503
|
}
|
|
464
504
|
}
|
|
465
505
|
if (id === 'system.config' && obj?.common?.language) {
|
|
@@ -494,9 +534,10 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
494
534
|
/* ignore */
|
|
495
535
|
});
|
|
496
536
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
537
|
+
// O(1) dispatch via pattern map instead of O(n) forEach
|
|
538
|
+
const objSubs = this.subscriptionsObjectMap.get(id);
|
|
539
|
+
if (objSubs) {
|
|
540
|
+
for (const sub of objSubs) {
|
|
500
541
|
try {
|
|
501
542
|
sub.callback(id, obj);
|
|
502
543
|
}
|
|
@@ -504,7 +545,7 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
504
545
|
this.log.error(`Error in callback: ${err.toString()}`);
|
|
505
546
|
}
|
|
506
547
|
}
|
|
507
|
-
}
|
|
548
|
+
}
|
|
508
549
|
// handle Script object updates
|
|
509
550
|
if (!obj && formerObj?.type === 'script') {
|
|
510
551
|
// Object Deleted just now
|
|
@@ -631,16 +672,16 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
631
672
|
if (this.adapterSubs[id]) {
|
|
632
673
|
const parts = id.split('.');
|
|
633
674
|
const a = `${parts[2]}.${parts[3]}`;
|
|
634
|
-
for (
|
|
635
|
-
this.log.info(`Detected coming adapter "${a}". Send subscribe: ${
|
|
636
|
-
this.sendTo(a, 'subscribe',
|
|
675
|
+
for (const sub of this.adapterSubs[id]) {
|
|
676
|
+
this.log.info(`Detected coming adapter "${a}". Send subscribe: ${sub}`);
|
|
677
|
+
this.sendTo(a, 'subscribe', sub);
|
|
637
678
|
}
|
|
638
679
|
}
|
|
639
680
|
}
|
|
640
681
|
}
|
|
641
|
-
else if ( /*!oldState && */!this.
|
|
642
|
-
this.
|
|
643
|
-
this.
|
|
682
|
+
else if ( /*!oldState && */!this.stateIdSet.has(id)) {
|
|
683
|
+
this.sortedInsert(id);
|
|
684
|
+
this.stateIdSet.add(id);
|
|
644
685
|
}
|
|
645
686
|
this.states[id] = state;
|
|
646
687
|
}
|
|
@@ -649,18 +690,48 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
649
690
|
delete this.states[id];
|
|
650
691
|
}
|
|
651
692
|
state = {};
|
|
652
|
-
const pos = this.stateIds
|
|
693
|
+
const pos = this.binaryIndexOf(this.stateIds, id);
|
|
653
694
|
if (pos !== -1) {
|
|
654
695
|
this.stateIds.splice(pos, 1);
|
|
696
|
+
this.stateIdSet.delete(id);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
// Collect matching subscriptions:
|
|
700
|
+
// 1. O(1) exact-id map lookup – only buckets for this specific state id
|
|
701
|
+
// 2. Linear scan over wildcard/regex subscriptions (unavoidable)
|
|
702
|
+
// EventObj is created lazily – only when at least one subscription must be dispatched.
|
|
703
|
+
const exactSubs = this.subscriptionsMap.get(id);
|
|
704
|
+
const wildcardSubs = this.subscriptionsWildcard;
|
|
705
|
+
const hasWork = (exactSubs && exactSubs.length > 0) || wildcardSubs.length > 0;
|
|
706
|
+
if (!hasWork) {
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
let _eventObj = null;
|
|
710
|
+
const getEvent = () => {
|
|
711
|
+
if (!_eventObj) {
|
|
712
|
+
_eventObj = (0, eventObj_1.createEventObject)(this.context, id, this.convertBackStringifiedValues(id, state), this.convertBackStringifiedValues(id, oldState));
|
|
713
|
+
}
|
|
714
|
+
return _eventObj;
|
|
715
|
+
};
|
|
716
|
+
if (exactSubs) {
|
|
717
|
+
for (let i = 0, l = exactSubs.length; i < l; i++) {
|
|
718
|
+
const sub = exactSubs[i];
|
|
719
|
+
if (sub?.patternCompareFunctions && patternMatching(getEvent(), sub.patternCompareFunctions)) {
|
|
720
|
+
try {
|
|
721
|
+
sub.callback(getEvent());
|
|
722
|
+
}
|
|
723
|
+
catch (err) {
|
|
724
|
+
this.log.error(`Error in callback: ${err.toString()}`);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
655
727
|
}
|
|
656
728
|
}
|
|
657
|
-
const _eventObj = (0, eventObj_1.createEventObject)(this.context, id, this.convertBackStringifiedValues(id, state), this.convertBackStringifiedValues(id, oldState));
|
|
658
729
|
// if this state matches any subscriptions
|
|
659
|
-
for (let i = 0, l =
|
|
660
|
-
const sub =
|
|
661
|
-
if (sub?.patternCompareFunctions && patternMatching(
|
|
730
|
+
for (let i = 0, l = wildcardSubs.length; i < l; i++) {
|
|
731
|
+
const sub = wildcardSubs[i];
|
|
732
|
+
if (sub?.patternCompareFunctions && patternMatching(getEvent(), sub.patternCompareFunctions)) {
|
|
662
733
|
try {
|
|
663
|
-
sub.callback(
|
|
734
|
+
sub.callback(getEvent());
|
|
664
735
|
}
|
|
665
736
|
catch (err) {
|
|
666
737
|
this.log.error(`Error in callback: ${err.toString()}`);
|
|
@@ -683,15 +754,22 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
683
754
|
}
|
|
684
755
|
}
|
|
685
756
|
async onUnload(callback) {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
757
|
+
try {
|
|
758
|
+
await this.debugStop();
|
|
759
|
+
this.stopTimeSchedules();
|
|
760
|
+
if (this.setStateCountCheckInterval) {
|
|
761
|
+
clearInterval(this.setStateCountCheckInterval);
|
|
762
|
+
this.setStateCountCheckInterval = null;
|
|
763
|
+
}
|
|
764
|
+
await this.stopAllScripts();
|
|
691
765
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
766
|
+
catch (err) {
|
|
767
|
+
this.log.error(`Error during unload: ${err.message}`);
|
|
768
|
+
}
|
|
769
|
+
finally {
|
|
770
|
+
if (typeof callback === 'function') {
|
|
771
|
+
callback();
|
|
772
|
+
}
|
|
695
773
|
}
|
|
696
774
|
}
|
|
697
775
|
async onReady() {
|
|
@@ -989,16 +1067,12 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
989
1067
|
const body = JSON.stringify(bodyObj);
|
|
990
1068
|
const bodyBuffer = Buffer.from(body, 'utf8');
|
|
991
1069
|
chatHeaders['Content-Length'] = bodyBuffer.length;
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
urlObj = new URL(url);
|
|
995
|
-
}
|
|
996
|
-
catch {
|
|
1070
|
+
const resolved = resolveRequestModule(url);
|
|
1071
|
+
if (!resolved) {
|
|
997
1072
|
this.sendTo(obj.from, obj.command, { error: `Invalid API URL: ${url}` }, obj.callback);
|
|
998
1073
|
break;
|
|
999
1074
|
}
|
|
1000
|
-
const isHttps =
|
|
1001
|
-
const requestModule = isHttps ? https : http;
|
|
1075
|
+
const { module: requestModule, isHttps } = resolved;
|
|
1002
1076
|
try {
|
|
1003
1077
|
const req = requestModule.request(url, {
|
|
1004
1078
|
method: 'POST',
|
|
@@ -1110,16 +1184,12 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1110
1184
|
testHeaders.Authorization = `Bearer ${apiKey}`;
|
|
1111
1185
|
}
|
|
1112
1186
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
urlObj = new URL(url);
|
|
1116
|
-
}
|
|
1117
|
-
catch {
|
|
1187
|
+
const resolved = resolveRequestModule(url);
|
|
1188
|
+
if (!resolved) {
|
|
1118
1189
|
this.sendTo(obj.from, obj.command, { error: `Invalid API URL: ${url}` }, obj.callback);
|
|
1119
1190
|
break;
|
|
1120
1191
|
}
|
|
1121
|
-
const isHttps =
|
|
1122
|
-
const requestModule = isHttps ? https : http;
|
|
1192
|
+
const { module: requestModule, isHttps } = resolved;
|
|
1123
1193
|
try {
|
|
1124
1194
|
const req = requestModule.request(url, {
|
|
1125
1195
|
method: 'GET',
|
|
@@ -1234,14 +1304,16 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1234
1304
|
}
|
|
1235
1305
|
}
|
|
1236
1306
|
onLog(msg) {
|
|
1237
|
-
Object.keys(this.logSubscriptions)
|
|
1238
|
-
|
|
1239
|
-
(handler.
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1307
|
+
for (const name of Object.keys(this.logSubscriptions)) {
|
|
1308
|
+
for (const handler of this.logSubscriptions[name]) {
|
|
1309
|
+
if (typeof handler.cb === 'function' &&
|
|
1310
|
+
(handler.severity === '*' || handler.severity === msg.severity)) {
|
|
1311
|
+
handler.sandbox.logHandler = handler.severity || '*';
|
|
1312
|
+
handler.cb.call(handler.sandbox, msg);
|
|
1313
|
+
handler.sandbox.logHandler = undefined;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1245
1317
|
}
|
|
1246
1318
|
logError(scriptName, msg, e, offs) {
|
|
1247
1319
|
const stack = e.stack ? e.stack.toString().split('\n') : e ? e.toString() : '';
|
|
@@ -1493,11 +1565,11 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1493
1565
|
}
|
|
1494
1566
|
}
|
|
1495
1567
|
}
|
|
1496
|
-
//
|
|
1568
|
+
// Check setState counter per minute and stop a script if too high
|
|
1497
1569
|
this.setStateCountCheckInterval = setInterval(() => {
|
|
1498
|
-
Object.keys(this.scripts)
|
|
1570
|
+
for (const id of Object.keys(this.scripts)) {
|
|
1499
1571
|
if (!this.scripts[id]) {
|
|
1500
|
-
|
|
1572
|
+
continue;
|
|
1501
1573
|
}
|
|
1502
1574
|
const currentSetStatePerMinuteCounter = this.scripts[id].setStatePerMinuteCounter;
|
|
1503
1575
|
this.scripts[id].setStatePerMinuteCounter = 0;
|
|
@@ -1515,8 +1587,8 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1515
1587
|
this.scripts[id].setStatePerMinuteProblemCounter--;
|
|
1516
1588
|
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
1589
|
}
|
|
1518
|
-
}
|
|
1519
|
-
},
|
|
1590
|
+
}
|
|
1591
|
+
}, 60_000).unref();
|
|
1520
1592
|
}
|
|
1521
1593
|
loadTypeScriptDeclarations() {
|
|
1522
1594
|
// try to load the typings on disk for all 3rd party modules
|
|
@@ -1534,10 +1606,15 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1534
1606
|
.split(/[,;\s]+/)
|
|
1535
1607
|
.map(s => s.trim())
|
|
1536
1608
|
.filter(s => !!s);
|
|
1609
|
+
// O(1) lookups – avoids O(n²) Array.includes inside loops
|
|
1610
|
+
const installedSet = new Set(installedLibs);
|
|
1611
|
+
const wantsSet = new Set(wantsTypings);
|
|
1612
|
+
const packagesSet = new Set(packages);
|
|
1537
1613
|
// Add all installed libraries the user has requested typings for to the list of packages
|
|
1538
1614
|
for (const lib of installedLibs) {
|
|
1539
|
-
if (
|
|
1615
|
+
if (wantsSet.has(lib) && !packagesSet.has(lib)) {
|
|
1540
1616
|
packages.push(lib);
|
|
1617
|
+
packagesSet.add(lib);
|
|
1541
1618
|
}
|
|
1542
1619
|
}
|
|
1543
1620
|
// Some packages have submodules (e.g., rxjs/operators) that are not exposed through the main entry point
|
|
@@ -1548,8 +1625,9 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1548
1625
|
continue;
|
|
1549
1626
|
}
|
|
1550
1627
|
const pkgName = lib.substring(0, lib.indexOf('/'));
|
|
1551
|
-
if (
|
|
1628
|
+
if (installedSet.has(pkgName) && !packagesSet.has(lib)) {
|
|
1552
1629
|
packages.push(lib);
|
|
1630
|
+
packagesSet.add(lib);
|
|
1553
1631
|
}
|
|
1554
1632
|
}
|
|
1555
1633
|
}
|
|
@@ -1583,43 +1661,38 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1583
1661
|
if (obj) {
|
|
1584
1662
|
// add state to state ID's list
|
|
1585
1663
|
if (obj.type === 'state') {
|
|
1586
|
-
if (!this.
|
|
1587
|
-
this.
|
|
1588
|
-
this.
|
|
1664
|
+
if (!this.stateIdSet.has(id)) {
|
|
1665
|
+
this.sortedInsert(id);
|
|
1666
|
+
this.stateIdSet.add(id);
|
|
1589
1667
|
}
|
|
1590
1668
|
if (this.context.devices && this.context.channels) {
|
|
1591
1669
|
const parts = id.split('.');
|
|
1592
1670
|
parts.pop();
|
|
1593
1671
|
const chn = parts.join('.');
|
|
1594
|
-
this.context.channels[chn] ||=
|
|
1595
|
-
this.context.channels[chn].
|
|
1672
|
+
this.context.channels[chn] ||= new Set();
|
|
1673
|
+
this.context.channels[chn].add(id);
|
|
1596
1674
|
parts.pop();
|
|
1597
1675
|
const dev = parts.join('.');
|
|
1598
|
-
this.context.devices[dev] ||=
|
|
1599
|
-
this.context.devices[dev].
|
|
1676
|
+
this.context.devices[dev] ||= new Set();
|
|
1677
|
+
this.context.devices[dev].add(id);
|
|
1600
1678
|
}
|
|
1601
1679
|
}
|
|
1602
1680
|
}
|
|
1603
1681
|
else {
|
|
1604
1682
|
// delete object from state ID's list
|
|
1605
|
-
const pos = this.stateIds
|
|
1683
|
+
const pos = this.binaryIndexOf(this.stateIds, id);
|
|
1606
1684
|
if (pos !== -1) {
|
|
1607
1685
|
this.stateIds.splice(pos, 1);
|
|
1686
|
+
this.stateIdSet.delete(id);
|
|
1608
1687
|
}
|
|
1609
1688
|
if (this.context.devices && this.context.channels) {
|
|
1610
1689
|
const parts = id.split('.');
|
|
1611
1690
|
parts.pop();
|
|
1612
1691
|
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
|
-
}
|
|
1692
|
+
this.context.channels[chn]?.delete(id);
|
|
1617
1693
|
parts.pop();
|
|
1618
1694
|
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
|
-
}
|
|
1695
|
+
this.context.devices[dev]?.delete(id);
|
|
1623
1696
|
}
|
|
1624
1697
|
delete this.folderCreationVerifiedObjects[id];
|
|
1625
1698
|
}
|
|
@@ -1744,11 +1817,12 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1744
1817
|
this.context.states = this.states;
|
|
1745
1818
|
this.addGetProperty(this.states);
|
|
1746
1819
|
}
|
|
1747
|
-
// remember all IDs
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1820
|
+
// remember all IDs – sort once to guarantee the sorted invariant
|
|
1821
|
+
// required by binaryIndexOf() / sortedInsert() used later
|
|
1822
|
+
const keys = Object.keys(res).sort();
|
|
1823
|
+
for (const id of keys) {
|
|
1824
|
+
this.stateIds.push(id);
|
|
1825
|
+
this.stateIdSet.add(id);
|
|
1752
1826
|
}
|
|
1753
1827
|
this.statesInitDone = true;
|
|
1754
1828
|
this.log.info('received all states');
|
|
@@ -1768,17 +1842,18 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1768
1842
|
this.objects = {};
|
|
1769
1843
|
this.context.objects = this.objects;
|
|
1770
1844
|
for (let i = 0; i < res.rows.length; i++) {
|
|
1771
|
-
|
|
1845
|
+
const doc = res.rows[i]?.doc;
|
|
1846
|
+
if (!doc) {
|
|
1772
1847
|
this.log.debug(`Got empty object for index ${i} (${res.rows[i].id})`);
|
|
1773
1848
|
continue;
|
|
1774
1849
|
}
|
|
1775
|
-
if (this.objects[
|
|
1850
|
+
if (this.objects[doc._id] === undefined) {
|
|
1776
1851
|
// If was already there, ignore
|
|
1777
|
-
this.objects[
|
|
1852
|
+
this.objects[doc._id] = doc;
|
|
1778
1853
|
}
|
|
1779
|
-
|
|
1854
|
+
doc.type === 'enum' && this._enums.add(doc._id);
|
|
1780
1855
|
// Collect all names
|
|
1781
|
-
this.addToNames(this.objects[
|
|
1856
|
+
this.addToNames(this.objects[doc._id]);
|
|
1782
1857
|
}
|
|
1783
1858
|
this.addGetProperty(this.objects);
|
|
1784
1859
|
const systemConfig = this.objects['system.config'];
|
|
@@ -1933,37 +2008,32 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1933
2008
|
}
|
|
1934
2009
|
this.names[name].push(id);
|
|
1935
2010
|
}
|
|
2011
|
+
// keep reverse-map up to date for O(1) getName()
|
|
2012
|
+
this.nameById.set(id, name);
|
|
1936
2013
|
}
|
|
1937
2014
|
}
|
|
1938
2015
|
removeFromNames(id) {
|
|
1939
2016
|
const n = this.getName(id);
|
|
1940
2017
|
if (n) {
|
|
1941
2018
|
if (Array.isArray(this.names[n])) {
|
|
1942
|
-
const
|
|
2019
|
+
const arr = this.names[n];
|
|
2020
|
+
const pos = arr.indexOf(id);
|
|
1943
2021
|
if (pos > -1) {
|
|
1944
|
-
|
|
1945
|
-
if (
|
|
1946
|
-
this.names[n] =
|
|
2022
|
+
arr.splice(pos, 1);
|
|
2023
|
+
if (arr.length === 1) {
|
|
2024
|
+
this.names[n] = arr[0];
|
|
1947
2025
|
}
|
|
1948
2026
|
}
|
|
1949
2027
|
}
|
|
1950
2028
|
else {
|
|
1951
2029
|
delete this.names[n];
|
|
1952
2030
|
}
|
|
2031
|
+
// keep reverse-map up to date for O(1) getName()
|
|
2032
|
+
this.nameById.delete(id);
|
|
1953
2033
|
}
|
|
1954
2034
|
}
|
|
1955
2035
|
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;
|
|
2036
|
+
return this.nameById.get(id) ?? null;
|
|
1967
2037
|
}
|
|
1968
2038
|
async installNpm(npmLib) {
|
|
1969
2039
|
return new Promise((resolve, reject) => {
|
|
@@ -1977,6 +2047,7 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1977
2047
|
const child = this.mods.child_process.exec(cmd, {
|
|
1978
2048
|
windowsHide: true,
|
|
1979
2049
|
cwd: path,
|
|
2050
|
+
timeout: 120_000, // 2 minutes max – prevents infinite blocking
|
|
1980
2051
|
});
|
|
1981
2052
|
child.stdout?.on('data', buf => this.log.info(buf.toString('utf8')));
|
|
1982
2053
|
child.stderr?.on('data', buf => this.log.error(buf.toString('utf8')));
|
|
@@ -2124,8 +2195,8 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
2124
2195
|
}
|
|
2125
2196
|
}
|
|
2126
2197
|
execute(script, name, engineType, verbose, debug) {
|
|
2127
|
-
script.intervals =
|
|
2128
|
-
script.timeouts =
|
|
2198
|
+
script.intervals = new Set();
|
|
2199
|
+
script.timeouts = new Set();
|
|
2129
2200
|
script.schedules = [];
|
|
2130
2201
|
script.wizards = [];
|
|
2131
2202
|
script.name = name;
|
|
@@ -2157,13 +2228,54 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
2157
2228
|
this.logError(name, 'Error by run:', err);
|
|
2158
2229
|
}
|
|
2159
2230
|
}
|
|
2231
|
+
/**
|
|
2232
|
+
* Finds the index of `id` in a sorted array using binary search – O(log n).
|
|
2233
|
+
* Returns -1 if not found. Used instead of Array.indexOf on stateIds.
|
|
2234
|
+
*/
|
|
2235
|
+
binaryIndexOf(arr, id) {
|
|
2236
|
+
let lo = 0;
|
|
2237
|
+
let hi = arr.length - 1;
|
|
2238
|
+
while (lo <= hi) {
|
|
2239
|
+
const mid = (lo + hi) >>> 1;
|
|
2240
|
+
if (arr[mid] === id) {
|
|
2241
|
+
return mid;
|
|
2242
|
+
}
|
|
2243
|
+
else if (arr[mid] < id) {
|
|
2244
|
+
lo = mid + 1;
|
|
2245
|
+
}
|
|
2246
|
+
else {
|
|
2247
|
+
hi = mid - 1;
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
return -1;
|
|
2251
|
+
}
|
|
2252
|
+
/**
|
|
2253
|
+
* Inserts `id` into the sorted `stateIds` array using binary search – O(log n).
|
|
2254
|
+
* Much faster than push() + sort() which is O(n log n) on every insertion.
|
|
2255
|
+
*/
|
|
2256
|
+
sortedInsert(id) {
|
|
2257
|
+
let lo = 0;
|
|
2258
|
+
let hi = this.stateIds.length;
|
|
2259
|
+
while (lo < hi) {
|
|
2260
|
+
const mid = (lo + hi) >>> 1;
|
|
2261
|
+
if (this.stateIds[mid] < id) {
|
|
2262
|
+
lo = mid + 1;
|
|
2263
|
+
}
|
|
2264
|
+
else {
|
|
2265
|
+
hi = mid;
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
if (this.stateIds[lo] !== id) {
|
|
2269
|
+
this.stateIds.splice(lo, 0, id);
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2160
2272
|
unsubscribe(id) {
|
|
2161
2273
|
if (!id) {
|
|
2162
2274
|
this.log.warn('unsubscribe: empty name');
|
|
2163
2275
|
return;
|
|
2164
2276
|
}
|
|
2165
2277
|
if (Array.isArray(id)) {
|
|
2166
|
-
id.forEach(sub => unsubscribe(sub));
|
|
2278
|
+
id.forEach(sub => this.unsubscribe(sub));
|
|
2167
2279
|
return;
|
|
2168
2280
|
}
|
|
2169
2281
|
if (id.constructor && id.constructor.name === 'RegExp') {
|
|
@@ -2180,11 +2292,8 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
2180
2292
|
const a = `${parts[0]}.${parts[1]}`;
|
|
2181
2293
|
const alive = `system.adapter.${a}.alive`;
|
|
2182
2294
|
if (this.adapterSubs[alive]) {
|
|
2183
|
-
|
|
2184
|
-
if (
|
|
2185
|
-
this.adapterSubs[alive].splice(pos, 1);
|
|
2186
|
-
}
|
|
2187
|
-
if (!this.adapterSubs[alive].length) {
|
|
2295
|
+
this.adapterSubs[alive].delete(id);
|
|
2296
|
+
if (!this.adapterSubs[alive].size) {
|
|
2188
2297
|
delete this.adapterSubs[alive];
|
|
2189
2298
|
}
|
|
2190
2299
|
}
|
|
@@ -2260,6 +2369,11 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
2260
2369
|
for (let i = this.subscriptions.length - 1; i >= 0; i--) {
|
|
2261
2370
|
if (this.subscriptions[i].name === name) {
|
|
2262
2371
|
const sub = this.subscriptions.splice(i, 1)[0];
|
|
2372
|
+
// Also remove from the O(1) dispatch structures – shared helper to keep the
|
|
2373
|
+
// exact-id classification identical to the subscribe side in sandbox.ts
|
|
2374
|
+
if (sub) {
|
|
2375
|
+
(0, sandbox_1.removeFromDispatchIndex)(this.context, sub);
|
|
2376
|
+
}
|
|
2263
2377
|
if (sub?.pattern.id) {
|
|
2264
2378
|
this.unsubscribe(sub.pattern.id);
|
|
2265
2379
|
}
|
|
@@ -2291,31 +2405,46 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
2291
2405
|
if (this.subscriptionsObject[i].name === name) {
|
|
2292
2406
|
const sub = this.subscriptionsObject.splice(i, 1)[0];
|
|
2293
2407
|
if (sub) {
|
|
2408
|
+
// Remove from O(1) dispatch map
|
|
2409
|
+
const mapSubs = this.subscriptionsObjectMap.get(sub.pattern);
|
|
2410
|
+
if (mapSubs) {
|
|
2411
|
+
const pos = mapSubs.indexOf(sub);
|
|
2412
|
+
if (pos !== -1) {
|
|
2413
|
+
mapSubs.splice(pos, 1);
|
|
2414
|
+
}
|
|
2415
|
+
if (!mapSubs.length) {
|
|
2416
|
+
this.subscriptionsObjectMap.delete(sub.pattern);
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2294
2419
|
this.unsubscribeForeignObjects(sub.pattern);
|
|
2295
2420
|
}
|
|
2296
2421
|
}
|
|
2297
2422
|
}
|
|
2298
2423
|
// Stop all timeouts
|
|
2299
|
-
for (
|
|
2300
|
-
clearTimeout(
|
|
2424
|
+
for (const t of this.scripts[name].timeouts) {
|
|
2425
|
+
clearTimeout(t);
|
|
2301
2426
|
}
|
|
2302
2427
|
// Stop all intervals
|
|
2303
|
-
for (
|
|
2304
|
-
clearInterval(
|
|
2305
|
-
}
|
|
2306
|
-
// Stop all delayed states (setStateDelayed timers)
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
this.timers[stateId].
|
|
2428
|
+
for (const t of this.scripts[name].intervals) {
|
|
2429
|
+
clearInterval(t);
|
|
2430
|
+
}
|
|
2431
|
+
// Stop all delayed states (setStateDelayed timers) – O(1) via reverse-index
|
|
2432
|
+
const scriptStateIds = this.timersByScript.get(name);
|
|
2433
|
+
if (scriptStateIds) {
|
|
2434
|
+
for (const stateId of scriptStateIds) {
|
|
2435
|
+
if (this.timers[stateId]) {
|
|
2436
|
+
for (let i = this.timers[stateId].length - 1; i >= 0; i--) {
|
|
2437
|
+
if (this.timers[stateId][i].scriptName === name) {
|
|
2438
|
+
clearTimeout(this.timers[stateId][i].t);
|
|
2439
|
+
this.timers[stateId].splice(i, 1);
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
if (!this.timers[stateId].length) {
|
|
2443
|
+
delete this.timers[stateId];
|
|
2313
2444
|
}
|
|
2314
|
-
}
|
|
2315
|
-
if (!this.timers[stateId].length) {
|
|
2316
|
-
delete this.timers[stateId];
|
|
2317
2445
|
}
|
|
2318
2446
|
}
|
|
2447
|
+
this.timersByScript.delete(name);
|
|
2319
2448
|
}
|
|
2320
2449
|
// Stop all scheduled jobs
|
|
2321
2450
|
for (let i = 0; i < this.scripts[name].schedules.length; i++) {
|
|
@@ -2562,6 +2691,11 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
2562
2691
|
this.timeScheduleTimer = setTimeout(() => this.timeSchedule(), interval);
|
|
2563
2692
|
}
|
|
2564
2693
|
async dayTimeSchedules() {
|
|
2694
|
+
// Always clear any existing timer to prevent memory leaks on rapid re-scheduling
|
|
2695
|
+
if (this.dayScheduleTimer) {
|
|
2696
|
+
clearTimeout(this.dayScheduleTimer);
|
|
2697
|
+
this.dayScheduleTimer = null;
|
|
2698
|
+
}
|
|
2565
2699
|
// get astrological event
|
|
2566
2700
|
if (this.config.latitude === undefined ||
|
|
2567
2701
|
this.config.longitude === undefined ||
|
|
@@ -2957,19 +3091,18 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
2957
3091
|
}
|
|
2958
3092
|
}
|
|
2959
3093
|
function patternMatching(event, patternFunctions) {
|
|
2960
|
-
|
|
3094
|
+
const logic = patternFunctions.logic ?? 'and';
|
|
2961
3095
|
for (let i = 0, len = patternFunctions.length; i < len; i++) {
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
}
|
|
2966
|
-
matched = true;
|
|
3096
|
+
const result = patternFunctions[i](event);
|
|
3097
|
+
if (logic === 'and' && !result) {
|
|
3098
|
+
return false; // short-circuit AND – one false is enough
|
|
2967
3099
|
}
|
|
2968
|
-
|
|
2969
|
-
return
|
|
3100
|
+
if (logic === 'or' && result) {
|
|
3101
|
+
return true; // short-circuit OR – one true is enough
|
|
2970
3102
|
}
|
|
2971
3103
|
}
|
|
2972
|
-
|
|
3104
|
+
// AND: all passed → true; OR: none matched → false
|
|
3105
|
+
return logic === 'and';
|
|
2973
3106
|
}
|
|
2974
3107
|
// If started as allInOne mode => return function to create an instance
|
|
2975
3108
|
if (require.main !== module) {
|