iobroker.javascript 8.9.2 → 9.0.1

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.
Files changed (154) hide show
  1. package/README.md +7 -3
  2. package/admin/assets/{createSvgIcon-BdRvDw53.js → createSvgIcon-BQT-7-GS.js} +1 -1
  3. package/admin/assets/{docs-6na4NFtj.js → docs-BrInh2Pn.js} +2 -2
  4. package/admin/assets/index-BzKS5vx-.js +1329 -0
  5. package/admin/assets/index-CfuHVX2B.js +193 -0
  6. package/admin/assets/index-CuCs5aLs.js +5 -0
  7. package/admin/assets/index-tExaxF69.js +930 -0
  8. package/admin/assets/inheritsLoose--KnFDOw1.js +36 -0
  9. package/admin/assets/{useTheme-Cl1QmYFL.js → useTheme-DF6qfrtI.js} +1 -1
  10. package/admin/custom/assets/{Components-ChpTW7sE.js → Components-D9kRsHNY.js} +1 -1
  11. package/admin/custom/assets/{DefaultPropsProvider-CHpMCMgA.js → DefaultPropsProvider-9qubdy-V.js} +2 -2
  12. package/admin/custom/assets/{FilledInput-CtcQVKyj.js → FilledInput-BpuM_GyB.js} +1 -1
  13. package/admin/custom/assets/Tabs-CvEv0KOT.js +138 -0
  14. package/admin/custom/assets/ThemeProvider-DZTpMR8n.js +29 -0
  15. package/admin/custom/assets/{createSvgIcon-C_vAELFU.js → createSvgIcon-BQThUM-f.js} +1 -1
  16. package/admin/custom/assets/hostInit-CrAs5YZS.js +1 -1
  17. package/admin/custom/assets/index-BXmu6lpn.js +904 -0
  18. package/admin/custom/assets/{index-DwsbskQy.js → index-DIWNdd4a.js} +1 -1
  19. package/admin/custom/assets/index-DZqbj5K4.js +55 -0
  20. package/admin/custom/assets/{index-D5apCzjJ.js → index-Dbe-II7_.js} +6 -6
  21. package/admin/custom/assets/index-sawv1cUe.js +1399 -0
  22. package/admin/custom/assets/{virtualExposes-BPncCdUy.js → virtualExposes-BMKIjWrE.js} +2 -2
  23. package/admin/custom/customComponents.js +2 -2
  24. package/admin/custom/i18n/de.json +29 -29
  25. package/admin/custom/i18n/es.json +29 -29
  26. package/admin/custom/i18n/fr.json +29 -29
  27. package/admin/custom/i18n/it.json +29 -29
  28. package/admin/custom/i18n/nl.json +29 -29
  29. package/admin/custom/i18n/pl.json +29 -29
  30. package/admin/custom/i18n/pt.json +29 -29
  31. package/admin/custom/i18n/ru.json +29 -29
  32. package/admin/custom/i18n/uk.json +29 -29
  33. package/admin/custom/i18n/zh-cn.json +29 -29
  34. package/admin/google-blockly/blockly_compressed.js +875 -887
  35. package/admin/google-blockly/blocks_compressed.js +38 -41
  36. package/admin/google-blockly/javascript_compressed.js +19 -54
  37. package/admin/google-blockly/media/delete-icon.svg +1 -0
  38. package/admin/google-blockly/media/foldout-icon.svg +1 -0
  39. package/admin/google-blockly/media/resize-handle.svg +3 -0
  40. package/admin/i18n/de.json +61 -0
  41. package/admin/i18n/en.json +61 -0
  42. package/admin/i18n/es.json +61 -0
  43. package/admin/i18n/fr.json +61 -0
  44. package/admin/i18n/it.json +61 -0
  45. package/admin/i18n/nl.json +61 -0
  46. package/admin/i18n/pl.json +61 -0
  47. package/admin/i18n/pt.json +61 -0
  48. package/admin/i18n/ru.json +61 -0
  49. package/admin/i18n/uk.json +61 -0
  50. package/admin/i18n/zh-cn.json +61 -0
  51. package/admin/jsonConfig.json +2 -0
  52. package/admin/mf-manifest.json +1 -1
  53. package/admin/remoteEntry.js +2 -2
  54. package/admin/tab.html +4 -4
  55. package/admin/vsFont/README.md +1 -1
  56. package/admin/vsFont/createJson.js +1 -1
  57. package/build-backend/lib/consts.js +346 -0
  58. package/build-backend/lib/consts.js.map +1 -0
  59. package/build-backend/lib/convert.js +120 -0
  60. package/build-backend/lib/convert.js.map +1 -0
  61. package/build-backend/lib/debug.js +163 -0
  62. package/build-backend/lib/debug.js.map +1 -0
  63. package/{lib → build-backend/lib}/debugger.js +261 -232
  64. package/build-backend/lib/debugger.js.map +1 -0
  65. package/build-backend/lib/eventObj.js +177 -0
  66. package/build-backend/lib/eventObj.js.map +1 -0
  67. package/build-backend/lib/inspect.js +728 -0
  68. package/build-backend/lib/inspect.js.map +1 -0
  69. package/build-backend/lib/javascript.d.ts +1972 -0
  70. package/{lib → build-backend/lib}/mirror.js +185 -182
  71. package/build-backend/lib/mirror.js.map +1 -0
  72. package/build-backend/lib/nodeModulesManagement.js +25 -0
  73. package/build-backend/lib/nodeModulesManagement.js.map +1 -0
  74. package/build-backend/lib/patternCompareFunctions.js +321 -0
  75. package/build-backend/lib/patternCompareFunctions.js.map +1 -0
  76. package/build-backend/lib/protectFs.js +477 -0
  77. package/build-backend/lib/protectFs.js.map +1 -0
  78. package/build-backend/lib/request.js +89 -0
  79. package/build-backend/lib/request.js.map +1 -0
  80. package/{lib → build-backend/lib}/sandbox.js +2046 -1852
  81. package/build-backend/lib/sandbox.js.map +1 -0
  82. package/build-backend/lib/scheduler.js +553 -0
  83. package/build-backend/lib/scheduler.js.map +1 -0
  84. package/build-backend/lib/tools.js +183 -0
  85. package/build-backend/lib/tools.js.map +1 -0
  86. package/build-backend/lib/typescriptSettings.js +39 -0
  87. package/build-backend/lib/typescriptSettings.js.map +1 -0
  88. package/build-backend/lib/typescriptTools.js +435 -0
  89. package/build-backend/lib/typescriptTools.js.map +1 -0
  90. package/build-backend/lib/utils.js +66 -0
  91. package/build-backend/lib/utils.js.map +1 -0
  92. package/{lib → build-backend/lib}/words.js +23 -30
  93. package/build-backend/lib/words.js.map +1 -0
  94. package/build-backend/main.js +2671 -0
  95. package/build-backend/main.js.map +1 -0
  96. package/build-backend/types.d.ts +742 -0
  97. package/docs/de/README.md +1 -1
  98. package/docs/en/README.md +1 -1
  99. package/docs/en/javascript.md +10 -6
  100. package/docs/en/upgrade-guide.md +5 -2
  101. package/docs/ru/README.md +1 -1
  102. package/install/installTypings.js +34 -47
  103. package/io-package.json +14 -14
  104. package/package.json +14 -14
  105. package/admin/assets/index-DCkkMRRa.js +0 -1335
  106. package/admin/assets/index-DJfSBoBB.js +0 -192
  107. package/admin/assets/index-DTiA4WdK.js +0 -930
  108. package/admin/assets/index-qABB1JVe.js +0 -5
  109. package/admin/assets/inheritsLoose-BW-3V39H.js +0 -36
  110. package/admin/custom/assets/Tabs-C1KOyWRi.js +0 -137
  111. package/admin/custom/assets/ThemeProvider-Buvx5nSd.js +0 -29
  112. package/admin/custom/assets/index-BGCRznKh.js +0 -904
  113. package/admin/custom/assets/index-DfBPYXqA.js +0 -55
  114. package/admin/custom/assets/index-jIr6kOLK.js +0 -1398
  115. package/admin/i18n/de/flat.txt +0 -427
  116. package/admin/i18n/de/translations.json +0 -429
  117. package/admin/i18n/en/flat.txt +0 -427
  118. package/admin/i18n/en/translations.json +0 -429
  119. package/admin/i18n/es/flat.txt +0 -427
  120. package/admin/i18n/es/translations.json +0 -429
  121. package/admin/i18n/flat.txt +0 -427
  122. package/admin/i18n/fr/flat.txt +0 -427
  123. package/admin/i18n/fr/translations.json +0 -429
  124. package/admin/i18n/it/flat.txt +0 -427
  125. package/admin/i18n/it/translations.json +0 -429
  126. package/admin/i18n/nl/flat.txt +0 -427
  127. package/admin/i18n/nl/translations.json +0 -429
  128. package/admin/i18n/pl/flat.txt +0 -427
  129. package/admin/i18n/pl/translations.json +0 -429
  130. package/admin/i18n/pt/flat.txt +0 -427
  131. package/admin/i18n/pt/translations.json +0 -429
  132. package/admin/i18n/ru/flat.txt +0 -427
  133. package/admin/i18n/ru/translations.json +0 -429
  134. package/admin/i18n/uk/flat.txt +0 -427
  135. package/admin/i18n/uk/translations.json +0 -429
  136. package/admin/i18n/zh-cn/flat.txt +0 -427
  137. package/admin/i18n/zh-cn/translations.json +0 -429
  138. package/lib/adapter-config.d.ts +0 -16
  139. package/lib/consts.js +0 -83
  140. package/lib/convert.js +0 -106
  141. package/lib/debug.js +0 -154
  142. package/lib/eventObj.js +0 -201
  143. package/lib/inspect.js +0 -688
  144. package/lib/javascript.d.ts +0 -1858
  145. package/lib/nodeModulesManagement.js +0 -26
  146. package/lib/patternCompareFunctions.js +0 -391
  147. package/lib/protectFs.js +0 -401
  148. package/lib/request.js +0 -72
  149. package/lib/scheduler.js +0 -447
  150. package/lib/tools.js +0 -192
  151. package/lib/typescriptSettings.js +0 -44
  152. package/lib/typescriptTools.js +0 -529
  153. package/lib/utils.js +0 -87
  154. package/main.js +0 -2653
@@ -0,0 +1,2671 @@
1
+ "use strict";
2
+ /*
3
+ * Javascript adapter
4
+ *
5
+ * The MIT License (MIT)
6
+ *
7
+ * Copyright (c) 2014-2024 bluefox <dogafox@gmail.com>,
8
+ *
9
+ * Copyright (c) 2014 hobbyquaker
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ var __importDefault = (this && this.__importDefault) || function (mod) {
45
+ return (mod && mod.__esModule) ? mod : { "default": mod };
46
+ };
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ const node_vm_1 = require("node:vm");
49
+ const node_fs_1 = require("node:fs");
50
+ const node_path_1 = require("node:path");
51
+ const node_child_process_1 = require("node:child_process");
52
+ const virtual_tsc_1 = require("virtual-tsc");
53
+ const node_util_1 = require("node:util");
54
+ const prettier_1 = __importDefault(require("prettier"));
55
+ const dgram = __importStar(require("node:dgram"));
56
+ const crypto = __importStar(require("node:crypto"));
57
+ const dns = __importStar(require("node:dns"));
58
+ const events = __importStar(require("node:events"));
59
+ const http = __importStar(require("node:http"));
60
+ const https = __importStar(require("node:https"));
61
+ const http2 = __importStar(require("node:http2"));
62
+ const net = __importStar(require("node:net"));
63
+ const os = __importStar(require("node:os"));
64
+ const path = __importStar(require("node:path"));
65
+ const util = __importStar(require("node:util"));
66
+ const child_process = __importStar(require("node:child_process"));
67
+ const stream = __importStar(require("node:stream"));
68
+ const zlib = __importStar(require("node:zlib"));
69
+ // @ts-expect-error no types available
70
+ const suncalc = __importStar(require("suncalc2"));
71
+ const axios = __importStar(require("axios"));
72
+ // @ts-expect-error no types available
73
+ const wake_on_lan = __importStar(require("wake_on_lan"));
74
+ const nodeSchedule = __importStar(require("node-schedule"));
75
+ const adapter_core_1 = require("@iobroker/adapter-core");
76
+ const mirror_1 = require("./lib/mirror");
77
+ const protectFs_1 = __importDefault(require("./lib/protectFs"));
78
+ const words_1 = require("./lib/words");
79
+ const sandbox_1 = require("./lib/sandbox");
80
+ const nodeModulesManagement_1 = require("./lib/nodeModulesManagement");
81
+ const eventObj_1 = require("./lib/eventObj");
82
+ const scheduler_1 = require("./lib/scheduler");
83
+ const typescriptSettings_1 = require("./lib/typescriptSettings");
84
+ const tools_1 = require("./lib/tools");
85
+ const typescriptTools_1 = require("./lib/typescriptTools");
86
+ /**
87
+ * List of forbidden Locations for a mirror directory
88
+ * relative to the default data directory
89
+ * ATTENTION: the same list is also located in index_m.html!!
90
+ */
91
+ const forbiddenMirrorLocations = [
92
+ 'backup-objects',
93
+ 'files',
94
+ 'backitup',
95
+ '../backups',
96
+ '../node_modules',
97
+ '../log',
98
+ ];
99
+ const packageJson = JSON.parse((0, node_fs_1.readFileSync)(`${__dirname}/../package.json`).toString());
100
+ const SCRIPT_CODE_MARKER = 'script.js.';
101
+ let webstormDebug;
102
+ const isCI = !!process.env.CI;
103
+ // ambient declarations for typescript
104
+ let tsAmbient;
105
+ // TypeScript's scripts are only recompiled if their source hash changes.
106
+ // If an adapter update fixes the compilation bugs, a user won't notice until the changes and re-saves the script.
107
+ // To avoid that, we also include the
108
+ // adapter version and TypeScript version in the hash
109
+ const tsSourceHashBase = `versions:adapter=${packageJson.version},typescript=${packageJson.dependencies.typescript}`;
110
+ // taken from here: https://stackoverflow.com/questions/11887934/how-to-check-if-dst-daylight-saving-time-is-in-effect-and-if-so-the-offset
111
+ function dstOffsetAtDate(dateInput) {
112
+ const fullYear = dateInput.getFullYear() | 0;
113
+ // "Leap Years are any year that can be exactly divided by 4 (2012, 2016, etc.)
114
+ // except if it can be exactly divided by 100, then it isn't (2100, 2200, etc.)
115
+ // except if it can be exactly divided by 400, then it is (2000, 2400)"
116
+ // (https://www.mathsisfun.com/leap-years.html).
117
+ const isLeapYear = ((fullYear & 3) | ((fullYear / 100) & 3)) === 0 ? 1 : 0;
118
+ // (fullYear & 3) = (fullYear % 4), but faster
119
+ //Alternative:var isLeapYear=(new Date(currentYear,1,29,12)).getDate()===29?1:0
120
+ const fullMonth = dateInput.getMonth() | 0;
121
+ return (
122
+ // 1. We know what the time since the Epoch really is
123
+ +dateInput - // same as the dateInput.getTime() method
124
+ // 2. We know what the time since the Epoch at the start of the year is
125
+ +new Date(fullYear, 0) - // day defaults to 1 if not explicitly zeroed
126
+ // 3. Now, subtract what we would expect the time to be if daylight savings
127
+ // did not exist. This yields the time-offset due to daylight savings.
128
+ // Calculate the day of the year in the Gregorian calendar
129
+ // The code below works based upon the facts of signed right shifts
130
+ // • (x) >> n: shifts n and fills in the n highest bits with 0s
131
+ // • (-x) >> n: shifts n and fills in the n highest bits with 1s
132
+ // (This assumes that x is a positive integer)
133
+ ((((-1 + // the first day in the year is day 1
134
+ (31 & (-fullMonth >> 4)) + // January // (-11)>>4 = -1
135
+ ((28 + isLeapYear) & ((1 - fullMonth) >> 4)) + // February
136
+ (31 & ((2 - fullMonth) >> 4)) + // March
137
+ (30 & ((3 - fullMonth) >> 4)) + // April
138
+ (31 & ((4 - fullMonth) >> 4)) + // May
139
+ (30 & ((5 - fullMonth) >> 4)) + // June
140
+ (31 & ((6 - fullMonth) >> 4)) + // July
141
+ (31 & ((7 - fullMonth) >> 4)) + // August
142
+ (30 & ((8 - fullMonth) >> 4)) + // September
143
+ (31 & ((9 - fullMonth) >> 4)) + // October
144
+ (30 & ((10 - fullMonth) >> 4)) + // November
145
+ // There are no months past December: the year rolls into the next.
146
+ // Thus, "fullMonth" is 0-based, so it will never be 12 in JavaScript
147
+ (dateInput.getDate() | 0)) & // get day of the month
148
+ 0xffff) *
149
+ 24 *
150
+ 60 + // 24 hours in a day, 60 minutes in an hour
151
+ (dateInput.getHours() & 0xff) * 60 + // 60 minutes in an hour
152
+ (dateInput.getMinutes() & 0xff)) |
153
+ 0) *
154
+ 60 *
155
+ 1000 - // 60 seconds in a minute * 1000 milliseconds in a second
156
+ (dateInput.getSeconds() & 0xff) * 1000 - // 1000 milliseconds in a second
157
+ dateInput.getMilliseconds());
158
+ }
159
+ const regExGlobalOld = /_global$/;
160
+ const regExGlobalNew = /script\.js\.global\./;
161
+ function checkIsGlobal(obj) {
162
+ return obj?.common && (regExGlobalOld.test(obj.common.name) || regExGlobalNew.test(obj._id));
163
+ }
164
+ function fileMatching(sub, id, fileName) {
165
+ if (sub.idRegEx) {
166
+ if (!sub.idRegEx.test(id)) {
167
+ return false;
168
+ }
169
+ }
170
+ else {
171
+ if (sub.id !== id) {
172
+ return false;
173
+ }
174
+ }
175
+ if (sub.fileRegEx) {
176
+ if (!sub.fileRegEx.test(fileName)) {
177
+ return false;
178
+ }
179
+ }
180
+ else {
181
+ if (sub.fileNamePattern !== fileName) {
182
+ return false;
183
+ }
184
+ }
185
+ return true;
186
+ }
187
+ function getNextTimeEvent(time, useNextDay) {
188
+ const now = getAstroStartOfDay();
189
+ const [timeHours, timeMinutes] = time.split(':');
190
+ const nTimeHours = parseInt(timeHours, 10);
191
+ const nTimeMinutes = parseInt(timeMinutes, 10);
192
+ if (useNextDay &&
193
+ (now.getHours() > nTimeHours || (now.getHours() === nTimeHours && now.getMinutes() > nTimeMinutes))) {
194
+ now.setDate(now.getDate() + 1);
195
+ }
196
+ now.setHours(nTimeHours);
197
+ now.setMinutes(nTimeMinutes);
198
+ return now;
199
+ }
200
+ function getAstroStartOfDay() {
201
+ const d = new Date();
202
+ d.setMinutes(0);
203
+ d.setSeconds(0);
204
+ d.setMilliseconds(0);
205
+ d.setTime(d.getTime() - d.getTimezoneOffset() * 60 * 1000);
206
+ d.setUTCHours(0);
207
+ return d;
208
+ }
209
+ function formatHoursMinutesSeconds(date) {
210
+ const h = String(date.getHours());
211
+ const m = String(date.getMinutes());
212
+ const s = String(date.getSeconds());
213
+ return `${h.padStart(2, '0')}:${m.padStart(2, '0')}:${s.padStart(2, '0')}`;
214
+ }
215
+ // Due to a npm bug, virtual-tsc may be hoisted to the top level node_modules but
216
+ // TypeScript may still be in the adapter level (https://npm.community/t/packages-with-peerdependencies-are-incorrectly-hoisted/4794),
217
+ // so we need to tell virtual-tsc where TypeScript is
218
+ (0, virtual_tsc_1.setTypeScriptResolveOptions)({
219
+ paths: [require.resolve('typescript')],
220
+ });
221
+ // compiler instance for global JS declarations
222
+ const jsDeclarationServer = new virtual_tsc_1.Server(typescriptSettings_1.jsDeclarationCompilerOptions, isCI ? false : undefined);
223
+ /**
224
+ * Stores the IDs of script objects whose change should be ignored because
225
+ * the compiled source was just updated
226
+ */
227
+ class JavaScript extends adapter_core_1.Adapter {
228
+ context;
229
+ errorLogFunction = {
230
+ error: (msg) => console.error(msg),
231
+ warn: (msg) => console.warn(msg),
232
+ info: (msg) => console.log(msg),
233
+ debug: (msg) => console.debug(msg),
234
+ silly: (msg) => console.debug(msg),
235
+ };
236
+ mods;
237
+ objectsInitDone = false;
238
+ statesInitDone = false;
239
+ objects = {};
240
+ states = {};
241
+ interimStateValues = {};
242
+ stateIds = [];
243
+ subscriptions = [];
244
+ subscriptionsFile = [];
245
+ subscriptionsObject = [];
246
+ subscribedPatterns = {};
247
+ subscribedPatternsFile = {};
248
+ adapterSubs = {};
249
+ timers = {};
250
+ _enums = [];
251
+ names = {}; // name: id
252
+ scripts = {};
253
+ messageBusHandlers = {};
254
+ logSubscriptions = {};
255
+ tempDirectories = {}; // name: path
256
+ folderCreationVerifiedObjects = {};
257
+ /** if logs are subscribed or not */
258
+ logSubscribed = false;
259
+ timeSettings = { format12: false, leadingZeros: true };
260
+ dayScheduleTimer = null; // schedule for astrological day
261
+ sunScheduleTimer = null; // schedule for sun moment times
262
+ timeScheduleTimer = null; // schedule for astrological day
263
+ activeStr = ''; // enabled state prefix
264
+ mirror;
265
+ stopCounters = {};
266
+ setStateCountCheckInterval = null;
267
+ globalScript = '';
268
+ /** Generated declarations for global TypeScripts */
269
+ globalDeclarations = '';
270
+ // Remember which definitions the global scripts
271
+ // have access to, because it depends on the compilation order
272
+ knownGlobalDeclarationsByScript = {};
273
+ globalScriptLines = 0;
274
+ // compiler instance for typescript
275
+ tsServer;
276
+ ignoreObjectChange = new Set();
277
+ debugState = {
278
+ scriptName: '',
279
+ child: null,
280
+ promiseOnEnd: null,
281
+ paused: false,
282
+ started: 0,
283
+ running: false,
284
+ };
285
+ constructor(options = {}) {
286
+ options = {
287
+ ...options,
288
+ name: 'javascript', // adapter name
289
+ useFormatDate: true,
290
+ /**
291
+ * If the JS-Controller catches an unhandled error, this will be called
292
+ * so we have a chance to handle it ourselves.
293
+ */
294
+ error: (err) => {
295
+ // Identify unhandled errors originating from callbacks in scripts
296
+ // These are not caught by wrapping the execution code in try-catch
297
+ if (err && typeof err.stack === 'string') {
298
+ const scriptCodeMarkerIndex = err.stack.indexOf(SCRIPT_CODE_MARKER);
299
+ if (scriptCodeMarkerIndex > -1) {
300
+ // This is a script error
301
+ let scriptName = err.stack.substring(scriptCodeMarkerIndex);
302
+ scriptName = scriptName.substring(0, scriptName.indexOf(':'));
303
+ this.logError(scriptName, 'Error:', err);
304
+ // Leave the script running for now
305
+ // signal to the JS-Controller that we handled the error ourselves
306
+ return true;
307
+ }
308
+ // check if a path contains adaptername but not own node_module
309
+ // this regex matched "iobroker.javascript/" if NOT followed by "node_modules"
310
+ if (!err.stack.match(/iobroker\.javascript[/\\](?!.*node_modules).*/g)) {
311
+ // This is an error without any info on origin (mostly async errors like connection errors)
312
+ // also consider it as being from a script
313
+ this.log.error('An error happened which is most likely from one of your scripts, but the originating script could not be detected.');
314
+ this.log.error(`Error: ${err.message}`);
315
+ this.log.error(err.stack);
316
+ // signal to the JS-Controller that we handled the error ourselves
317
+ return true;
318
+ }
319
+ }
320
+ return false;
321
+ },
322
+ };
323
+ super(options);
324
+ this.on('objectChange', this.onObjectChange.bind(this));
325
+ this.on('stateChange', this.onStateChange.bind(this));
326
+ this.on('ready', this.onReady.bind(this));
327
+ this.on('message', this.onMessage.bind(this));
328
+ this.on('unload', this.onUnload.bind(this));
329
+ this.on('fileChange', this.onFileChange.bind(this));
330
+ this.on('log', this.onLog.bind(this));
331
+ this.mods = {
332
+ fs: {},
333
+ 'fs/promises': {},
334
+ dgram,
335
+ crypto,
336
+ dns,
337
+ events,
338
+ http,
339
+ https,
340
+ http2,
341
+ net,
342
+ os,
343
+ path,
344
+ util,
345
+ child_process,
346
+ stream,
347
+ zlib,
348
+ suncalc,
349
+ axios,
350
+ wake_on_lan,
351
+ nodeSchedule,
352
+ };
353
+ // check the webstorm debug and just debug modes
354
+ let debugMode;
355
+ if (process.argv) {
356
+ for (let a = 1; a < process.argv.length; a++) {
357
+ if (process.argv[a].startsWith('--webstorm')) {
358
+ webstormDebug = process.argv[a].replace(/^(.*?=\s*)/, '');
359
+ }
360
+ if (process.argv[a] === '--debugScript') {
361
+ if (!process.argv[a + 1]) {
362
+ console.log('No script name provided');
363
+ process.exit(300);
364
+ }
365
+ else {
366
+ debugMode = process.argv[a + 1];
367
+ }
368
+ }
369
+ }
370
+ }
371
+ this.context = {
372
+ mods: this.mods,
373
+ objects: this.objects,
374
+ states: this.states,
375
+ interimStateValues: this.interimStateValues,
376
+ stateIds: this.stateIds,
377
+ errorLogFunction: this.errorLogFunction,
378
+ subscriptions: this.subscriptions,
379
+ subscriptionsFile: this.subscriptionsFile,
380
+ subscriptionsObject: this.subscriptionsObject,
381
+ subscribedPatterns: this.subscribedPatterns,
382
+ subscribedPatternsFile: this.subscribedPatternsFile,
383
+ adapterSubs: this.adapterSubs,
384
+ cacheObjectEnums: {},
385
+ timers: this.timers,
386
+ enums: this._enums,
387
+ names: this.names,
388
+ scripts: this.scripts,
389
+ messageBusHandlers: this.messageBusHandlers,
390
+ logSubscriptions: this.logSubscriptions,
391
+ tempDirectories: this.tempDirectories,
392
+ folderCreationVerifiedObjects: this.folderCreationVerifiedObjects,
393
+ isEnums: false, // If some subscription wants enum
394
+ channels: null,
395
+ devices: null,
396
+ logWithLineInfo: this.logWithLineInfo.bind(this),
397
+ scheduler: null,
398
+ timerId: 0,
399
+ rulesOpened: null, // opened rules
400
+ language: this.language || 'en',
401
+ updateLogSubscriptions: this.updateLogSubscriptions.bind(this),
402
+ convertBackStringifiedValues: this.convertBackStringifiedValues.bind(this),
403
+ updateObjectContext: this.updateObjectContext.bind(this),
404
+ prepareStateObject: this.prepareStateObject.bind(this),
405
+ debugMode,
406
+ getAbsoluteDefaultDataDir: adapter_core_1.getAbsoluteDefaultDataDir,
407
+ adapter: this,
408
+ logError: this.logError.bind(this),
409
+ };
410
+ this.tsServer = new virtual_tsc_1.Server(typescriptSettings_1.tsCompilerOptions, this.tsLog);
411
+ }
412
+ async onObjectChange(id, obj) {
413
+ // Check if we should ignore this change (once!) because we just updated the compiled sources
414
+ if (this.ignoreObjectChange.has(id)) {
415
+ // Update the cached script object and do nothing more
416
+ this.objects[id] = obj;
417
+ this.ignoreObjectChange.delete(id);
418
+ return;
419
+ }
420
+ // When still in initializing: already remember current values,
421
+ // but data structures are initialized elsewhere
422
+ if (!this.objectsInitDone) {
423
+ if (obj) {
424
+ this.objects[id] = obj;
425
+ }
426
+ return;
427
+ }
428
+ if (id.startsWith('enum.')) {
429
+ // clear cache
430
+ this.context.cacheObjectEnums = {};
431
+ // update this._enums array
432
+ if (obj) {
433
+ // If new
434
+ if (!this._enums.includes(id)) {
435
+ this._enums.push(id);
436
+ this._enums.sort();
437
+ }
438
+ }
439
+ else {
440
+ const pos = this._enums.indexOf(id);
441
+ // if deleted
442
+ if (pos !== -1) {
443
+ this._enums.splice(pos, 1);
444
+ }
445
+ }
446
+ }
447
+ if (id === 'system.config' && obj?.common?.language) {
448
+ // set language for debug messages
449
+ (0, words_1.setLanguage)(obj.common.language);
450
+ this.language = obj.common.language;
451
+ this.context.language = this.language;
452
+ }
453
+ // update stored time format for variables.dayTime
454
+ if (id === `${this.namespace}.variables.dayTime` && obj?.native) {
455
+ this.timeSettings.format12 = obj.native.format12 || false;
456
+ this.timeSettings.leadingZeros = obj.native.leadingZeros === undefined ? true : obj.native.leadingZeros;
457
+ }
458
+ // send changes to disk mirror
459
+ this.mirror?.onObjectChange(id, obj);
460
+ const formerObj = this.objects[id];
461
+ this.updateObjectContext(id, obj); // Update all Meta object data
462
+ // for the alias object changes on the state objects, we need to manually update the
463
+ // state cache value, because the new value is only published on the next change
464
+ if (obj?.type === 'state' && id.startsWith('alias.0.')) {
465
+ // execute async for speed
466
+ this.getForeignStateAsync(id)
467
+ .then(state => {
468
+ if (state) {
469
+ this.states[id] = state;
470
+ }
471
+ else if (this.states[id] !== undefined) {
472
+ delete this.states[id];
473
+ }
474
+ })
475
+ .catch(() => {
476
+ /* ignore */
477
+ });
478
+ }
479
+ this.subscriptionsObject.forEach(sub => {
480
+ // ToDo: implement comparing with id.0.* too
481
+ if (sub.pattern === id) {
482
+ try {
483
+ sub.callback(id, obj);
484
+ }
485
+ catch (err) {
486
+ this.log.error(`Error in callback: ${err.toString()}`);
487
+ }
488
+ }
489
+ });
490
+ // handle Script object updates
491
+ if (!obj && formerObj?.type === 'script') {
492
+ // Object Deleted just now
493
+ if (checkIsGlobal(formerObj)) {
494
+ // it was a global Script, and it was enabled and is now deleted => restart adapter
495
+ if (formerObj.common.enabled) {
496
+ this.log.info(`Active global Script ${id} deleted. Restart instance.`);
497
+ this.restart();
498
+ }
499
+ }
500
+ else if (formerObj.common?.engine === `system.adapter.${this.namespace}`) {
501
+ // It was a non-global Script and deleted => stop and remove it
502
+ await this.stopScript(id);
503
+ // delete scriptEnabled.blabla variable
504
+ const idActive = `scriptEnabled.${id.substring(SCRIPT_CODE_MARKER.length)}`;
505
+ await this.delStateAsync(idActive);
506
+ await this.delObjectAsync(idActive);
507
+ // delete scriptProblem.blabla variable
508
+ const idProblem = `scriptProblem.${id.substring(SCRIPT_CODE_MARKER.length)}`;
509
+ await this.delStateAsync(idProblem);
510
+ await this.delObjectAsync(idProblem);
511
+ }
512
+ }
513
+ else if (!formerObj && obj?.type === 'script') {
514
+ // New script that does not exist before
515
+ if (checkIsGlobal(obj)) {
516
+ // new global script added => restart adapter
517
+ if (obj.common.enabled) {
518
+ this.log.info(`Active global Script ${id} created. Restart instance.`);
519
+ this.restart();
520
+ }
521
+ }
522
+ else if (obj.common?.engine === `system.adapter.${this.namespace}`) {
523
+ // new non-global script - create states for scripts
524
+ await this.createActiveObject(id, !!obj.common.enabled);
525
+ await this.createProblemObject(id);
526
+ if (obj.common.enabled) {
527
+ // if enabled => Start script
528
+ await this.loadScriptById(id);
529
+ }
530
+ }
531
+ }
532
+ else if (obj?.type === 'script' && formerObj?.common) {
533
+ // Script changed ...
534
+ if (checkIsGlobal(obj)) {
535
+ if (obj.common.enabled || formerObj.common.enabled) {
536
+ this.log.info(`Global Script ${id} updated. Restart instance.`);
537
+ this.restart();
538
+ }
539
+ }
540
+ else {
541
+ // No global script
542
+ if (obj.common?.engine === `system.adapter.${this.namespace}`) {
543
+ // create states for scripts
544
+ await this.createActiveObject(id, !!obj.common.enabled);
545
+ await this.createProblemObject(id);
546
+ }
547
+ if ((formerObj.common.enabled && !obj.common.enabled) ||
548
+ (formerObj.common.engine === `system.adapter.${this.namespace}` &&
549
+ obj.common.engine !== `system.adapter.${this.namespace}`)) {
550
+ // Script disabled
551
+ if (formerObj.common.enabled && formerObj.common.engine === `system.adapter.${this.namespace}`) {
552
+ // Remove it from executing
553
+ await this.stopScript(id);
554
+ }
555
+ }
556
+ else if ((!formerObj.common.enabled && obj.common.enabled) ||
557
+ (formerObj.common.engine !== `system.adapter.${this.namespace}` &&
558
+ obj.common.engine === `system.adapter.${this.namespace}`)) {
559
+ // Script enabled
560
+ if (obj.common.enabled && obj.common.engine === `system.adapter.${this.namespace}`) {
561
+ // Start script
562
+ await this.loadScriptById(id);
563
+ }
564
+ }
565
+ else {
566
+ // if (obj.common.source !== formerObj.common.source) {
567
+ // Source changed => restart the script
568
+ this.stopCounters[id] = this.stopCounters[id] ? this.stopCounters[id] + 1 : 1;
569
+ void this.stopScript(id).then(() => {
570
+ // only start again after stop when "last" object change to prevent problems on
571
+ // multiple changes in fast frequency
572
+ if (!--this.stopCounters[id]) {
573
+ void this.loadScriptById(id);
574
+ }
575
+ });
576
+ }
577
+ }
578
+ }
579
+ }
580
+ onStateChange(id, state) {
581
+ if (this.interimStateValues[id] !== undefined) {
582
+ // any update invalidates the remembered interim value
583
+ delete this.interimStateValues[id];
584
+ }
585
+ if (!id || id.startsWith('messagebox.') || id.startsWith('log.')) {
586
+ return;
587
+ }
588
+ if (id === `${this.namespace}.debug.to` && state && !state.ack) {
589
+ if (!this.context.debugMode) {
590
+ this.debugSendToInspector(state.val);
591
+ }
592
+ return;
593
+ }
594
+ // When still in initializing: already remember current values,
595
+ // but data structures are initialized elsewhere
596
+ if (!this.statesInitDone) {
597
+ if (state) {
598
+ this.states[id] = state;
599
+ }
600
+ return;
601
+ }
602
+ const oldState = this.states[id];
603
+ if (state) {
604
+ if (oldState) {
605
+ // enable or disable script
606
+ if (!state.ack && id.startsWith(this.activeStr) && this.objects[id]?.native?.script) {
607
+ void this.extendForeignObject(this.objects[id].native.script, {
608
+ common: { enabled: state.val },
609
+ });
610
+ }
611
+ // monitor if adapter is alive and send all subscriptions once more, after adapter goes online
612
+ if ( /*oldState && */oldState.val === false && state.val && id.endsWith('.alive')) {
613
+ if (this.adapterSubs[id]) {
614
+ const parts = id.split('.');
615
+ const a = `${parts[2]}.${parts[3]}`;
616
+ for (let t = 0; t < this.adapterSubs[id].length; t++) {
617
+ this.log.info(`Detected coming adapter "${a}". Send subscribe: ${this.adapterSubs[id][t]}`);
618
+ this.sendTo(a, 'subscribe', this.adapterSubs[id][t]);
619
+ }
620
+ }
621
+ }
622
+ }
623
+ else if ( /*!oldState && */!this.stateIds.includes(id)) {
624
+ this.stateIds.push(id);
625
+ this.stateIds.sort();
626
+ }
627
+ this.states[id] = state;
628
+ }
629
+ else {
630
+ if (oldState) {
631
+ delete this.states[id];
632
+ }
633
+ state = {};
634
+ const pos = this.stateIds.indexOf(id);
635
+ if (pos !== -1) {
636
+ this.stateIds.splice(pos, 1);
637
+ }
638
+ }
639
+ const _eventObj = (0, eventObj_1.createEventObject)(this.context, id, this.convertBackStringifiedValues(id, state), this.convertBackStringifiedValues(id, oldState));
640
+ // if this state matches any subscriptions
641
+ for (let i = 0, l = this.subscriptions.length; i < l; i++) {
642
+ const sub = this.subscriptions[i];
643
+ if (sub?.patternCompareFunctions && patternMatching(_eventObj, sub.patternCompareFunctions)) {
644
+ try {
645
+ sub.callback(_eventObj);
646
+ }
647
+ catch (err) {
648
+ this.log.error(`Error in callback: ${err.toString()}`);
649
+ }
650
+ }
651
+ }
652
+ }
653
+ onFileChange(id, fileName, size) {
654
+ // if this file matches any subscriptions
655
+ for (let i = 0, l = this.subscriptionsFile.length; i < l; i++) {
656
+ const sub = this.subscriptionsFile[i];
657
+ if (sub && fileMatching(sub, id, fileName)) {
658
+ try {
659
+ sub.callback(id, fileName, size, sub.withFile);
660
+ }
661
+ catch (err) {
662
+ this.log.error(`Error in callback: ${err.toString()}`);
663
+ }
664
+ }
665
+ }
666
+ }
667
+ async onUnload(callback) {
668
+ await this.debugStop();
669
+ this.stopTimeSchedules();
670
+ if (this.setStateCountCheckInterval) {
671
+ clearInterval(this.setStateCountCheckInterval);
672
+ this.setStateCountCheckInterval = null;
673
+ }
674
+ await this.stopAllScripts();
675
+ if (typeof callback === 'function') {
676
+ callback();
677
+ }
678
+ }
679
+ async onReady() {
680
+ this.errorLogFunction = this.log;
681
+ this.context.errorLogFunction = this.log;
682
+ this.config.maxSetStatePerMinute = parseInt(this.config.maxSetStatePerMinute, 10) || 1000;
683
+ this.config.maxTriggersPerScript = parseInt(this.config.maxTriggersPerScript, 10) || 100;
684
+ if (this.supportsFeature && this.supportsFeature('PLUGINS')) {
685
+ const sentryInstance = this.getPluginInstance('sentry');
686
+ if (sentryInstance) {
687
+ const Sentry = sentryInstance.getSentryObject();
688
+ if (Sentry) {
689
+ const scope = Sentry.getCurrentScope();
690
+ scope.addEventProcessor((event, _hint) => {
691
+ if (event.exception?.values?.[0]) {
692
+ const eventData = event.exception.values[0];
693
+ if (eventData.stacktrace?.frames &&
694
+ Array.isArray(eventData.stacktrace.frames) &&
695
+ eventData.stacktrace.frames.length) {
696
+ // Exclude event if script Marker is included
697
+ if (eventData.stacktrace.frames.find(frame => frame.filename?.includes(SCRIPT_CODE_MARKER))) {
698
+ return null;
699
+ }
700
+ //Exclude event if own directory is included but not inside own node_modules
701
+ const ownNodeModulesDir = (0, node_path_1.join)(__dirname, 'node_modules');
702
+ if (!eventData.stacktrace.frames.find(frame => frame.filename &&
703
+ frame.filename.includes(__dirname) &&
704
+ !frame.filename.includes(ownNodeModulesDir))) {
705
+ return null;
706
+ }
707
+ // We have exception data and did not sort it out, so report it
708
+ return event;
709
+ }
710
+ }
711
+ // No exception in it ... do not report
712
+ return null;
713
+ });
714
+ }
715
+ }
716
+ }
717
+ await this.main();
718
+ }
719
+ onMessage(obj) {
720
+ switch (obj?.command) {
721
+ // process messageTo commands
722
+ case 'toScript':
723
+ case 'jsMessageBus':
724
+ if (obj.message &&
725
+ (obj.message.instance === null ||
726
+ obj.message.instance === undefined ||
727
+ `javascript.${obj.message.instance}` === this.namespace ||
728
+ obj.message.instance === this.namespace)) {
729
+ Object.keys(this.messageBusHandlers).forEach(name => {
730
+ // script name could be script.js.xxx or only xxx
731
+ if ((!obj.message.script || obj.message.script === name) &&
732
+ this.messageBusHandlers[name][obj.message.message]) {
733
+ this.messageBusHandlers[name][obj.message.message].forEach(handler => {
734
+ const sandbox = handler.sandbox;
735
+ sandbox.verbose && sandbox.log(`onMessage: ${JSON.stringify(obj.message)}`, 'info');
736
+ try {
737
+ if (obj.callback) {
738
+ handler.cb.call(sandbox, obj.message.data, (result) => {
739
+ if (sandbox.verbose) {
740
+ sandbox.log(`onMessage result: ${JSON.stringify(result)}`, 'info');
741
+ }
742
+ this.sendTo(obj.from, obj.command, result, obj.callback);
743
+ });
744
+ }
745
+ else {
746
+ handler.cb.call(sandbox, obj.message.data, (result) => {
747
+ sandbox.verbose &&
748
+ sandbox.log(`onMessage result: ${JSON.stringify(result)}`, 'info');
749
+ });
750
+ }
751
+ }
752
+ catch (err) {
753
+ void this.setState(`scriptProblem.${name.substring(SCRIPT_CODE_MARKER.length)}`, true, true);
754
+ this.logError(name, 'Error in callback:', err);
755
+ }
756
+ });
757
+ }
758
+ });
759
+ }
760
+ break;
761
+ case 'loadTypings': {
762
+ // Load typings for the editor
763
+ const typings = {};
764
+ // try to load TypeScript lib files from disk
765
+ try {
766
+ const typescriptLibs = (0, typescriptTools_1.resolveTypescriptLibs)(typescriptSettings_1.targetTsLib);
767
+ Object.assign(typings, typescriptLibs);
768
+ }
769
+ catch {
770
+ /* ok, no lib then */
771
+ }
772
+ // provide the already-loaded ioBroker typings and global script declarations
773
+ Object.assign(typings, tsAmbient);
774
+ // also provide the known global declarations for each global script
775
+ for (const globalScriptPaths of Object.keys(this.knownGlobalDeclarationsByScript)) {
776
+ typings[`${globalScriptPaths}.d.ts`] = this.knownGlobalDeclarationsByScript[globalScriptPaths];
777
+ }
778
+ if (obj.callback) {
779
+ this.sendTo(obj.from, obj.command, { typings }, obj.callback);
780
+ }
781
+ break;
782
+ }
783
+ case 'calcAstroAll': {
784
+ if (obj.message) {
785
+ const sunriseOffset = parseInt(obj.message.sunriseOffset === undefined
786
+ ? this.config.sunriseOffset
787
+ : obj.message.sunriseOffset, 10) || 0;
788
+ const sunsetOffset = parseInt(obj.message.sunsetOffset === undefined
789
+ ? this.config.sunsetOffset
790
+ : obj.message.sunsetOffset, 10) || 0;
791
+ const longitude = parseFloat(obj.message.longitude === undefined ? this.config.longitude : obj.message.longitude) || 0;
792
+ const latitude = parseFloat(obj.message.latitude === undefined ? this.config.latitude : obj.message.latitude) ||
793
+ 0;
794
+ const today = getAstroStartOfDay();
795
+ let astroEvents = {};
796
+ try {
797
+ astroEvents = this.mods.suncalc.getTimes(today, latitude, longitude);
798
+ }
799
+ catch (err) {
800
+ this.log.error(`Cannot calculate astro data: ${err}`);
801
+ }
802
+ if (astroEvents) {
803
+ try {
804
+ astroEvents.nextSunrise = this.getAstroEvent(today, obj.message.sunriseEvent || this.config.sunriseEvent, obj.message.sunriseLimitStart || this.config.sunriseLimitStart, obj.message.sunriseLimitEnd || this.config.sunriseLimitEnd, sunriseOffset, false, latitude, longitude, true);
805
+ astroEvents.nextSunset = this.getAstroEvent(today, obj.message.sunsetEvent || this.config.sunsetEvent, obj.message.sunsetLimitStart || this.config.sunsetLimitStart, obj.message.sunsetLimitEnd || this.config.sunsetLimitEnd, sunsetOffset, true, latitude, longitude, true);
806
+ }
807
+ catch (err) {
808
+ this.log.error(`Cannot calculate astro data: ${err}`);
809
+ }
810
+ }
811
+ const result = {};
812
+ const keys = Object.keys(astroEvents).sort((a, b) => astroEvents[a] -
813
+ astroEvents[b]);
814
+ keys.forEach(key => {
815
+ const validDate = astroEvents[key] !== null &&
816
+ !isNaN(astroEvents[key].getTime());
817
+ result[key] = {
818
+ isValidDate: validDate,
819
+ serverTime: validDate
820
+ ? formatHoursMinutesSeconds(astroEvents[key])
821
+ : 'n/a',
822
+ date: validDate
823
+ ? astroEvents[key].toISOString()
824
+ : 'n/a',
825
+ };
826
+ });
827
+ if (obj.callback) {
828
+ this.sendTo(obj.from, obj.command, result, obj.callback);
829
+ }
830
+ }
831
+ break;
832
+ }
833
+ case 'calcAstro': {
834
+ if (obj.message) {
835
+ const longitude = parseFloat(obj.message.longitude === undefined ? this.config.longitude : obj.message.longitude) || 0;
836
+ const latitude = parseFloat(obj.message.latitude === undefined ? this.config.latitude : obj.message.latitude) ||
837
+ 0;
838
+ const today = getAstroStartOfDay();
839
+ const sunriseEvent = obj.message?.sunriseEvent || this.config.sunriseEvent;
840
+ const sunriseLimitStart = obj.message?.sunriseLimitStart || this.config.sunriseLimitStart;
841
+ const sunriseLimitEnd = obj.message?.sunriseLimitEnd || this.config.sunriseLimitEnd;
842
+ const sunriseOffset = parseInt(obj.message.sunriseOffset === undefined
843
+ ? this.config.sunriseOffset
844
+ : obj.message.sunriseOffset, 10) || 0;
845
+ const nextSunrise = this.getAstroEvent(today, sunriseEvent, sunriseLimitStart, sunriseLimitEnd, sunriseOffset, false, latitude, longitude, true);
846
+ const sunsetEvent = obj.message?.sunsetEvent || this.config.sunsetEvent;
847
+ const sunsetLimitStart = obj.message?.sunsetLimitStart || this.config.sunsetLimitStart;
848
+ const sunsetLimitEnd = obj.message?.sunsetLimitEnd || this.config.sunsetLimitEnd;
849
+ const sunsetOffset = parseInt(obj.message.sunsetOffset === undefined
850
+ ? this.config.sunsetOffset
851
+ : obj.message.sunsetOffset, 10) || 0;
852
+ const nextSunset = this.getAstroEvent(today, sunsetEvent, sunsetLimitStart, sunsetLimitEnd, sunsetOffset, true, latitude, longitude, true);
853
+ const validDateSunrise = nextSunrise !== null && !isNaN(nextSunrise.getTime());
854
+ const validDateSunset = nextSunset !== null && !isNaN(nextSunset.getTime());
855
+ this.log.debug(`calcAstro sunrise: ${sunriseEvent} -> start ${sunriseLimitStart}, end: ${sunriseLimitEnd}, offset: ${sunriseOffset} - ${validDateSunrise ? nextSunrise.toISOString() : 'n/a'}`);
856
+ this.log.debug(`calcAstro sunset: ${sunsetEvent} -> start ${sunsetLimitStart}, end: ${sunsetLimitEnd}, offset: ${sunsetOffset} - ${validDateSunset ? nextSunset.toISOString() : 'n/a'}`);
857
+ if (obj.callback) {
858
+ this.sendTo(obj.from, obj.command, {
859
+ nextSunrise: {
860
+ isValidDate: validDateSunrise,
861
+ serverTime: validDateSunrise ? formatHoursMinutesSeconds(nextSunrise) : 'n/a',
862
+ date: nextSunrise.toISOString(),
863
+ },
864
+ nextSunset: {
865
+ isValidDate: validDateSunset,
866
+ serverTime: validDateSunset ? formatHoursMinutesSeconds(nextSunset) : 'n/a',
867
+ date: nextSunset.toISOString(),
868
+ },
869
+ }, obj.callback);
870
+ }
871
+ }
872
+ break;
873
+ }
874
+ case 'debug': {
875
+ if (!this.context.debugMode) {
876
+ this.debugStart(obj.message);
877
+ }
878
+ break;
879
+ }
880
+ case 'debugStop': {
881
+ if (!this.context.debugMode) {
882
+ void this.debugStop().then(() => console.log('stopped'));
883
+ }
884
+ break;
885
+ }
886
+ case 'rulesOn': {
887
+ this.context.rulesOpened = obj.message;
888
+ console.log(`Enable messaging for ${this.context.rulesOpened}`);
889
+ break;
890
+ }
891
+ case 'rulesOff': {
892
+ // maybe if (context.rulesOpened === obj.message)
893
+ console.log(`Disable messaging for ${this.context.rulesOpened}`);
894
+ this.context.rulesOpened = null;
895
+ break;
896
+ }
897
+ case 'getIoBrokerDataDir': {
898
+ if (obj.callback) {
899
+ this.sendTo(obj.from, obj.command, {
900
+ dataDir: (0, adapter_core_1.getAbsoluteDefaultDataDir)(),
901
+ sep: node_path_1.sep,
902
+ }, obj.callback);
903
+ }
904
+ break;
905
+ }
906
+ case 'prettier': {
907
+ // Format the code with Prettier
908
+ if (obj.message && typeof obj.message.code === 'string') {
909
+ try {
910
+ prettier_1.default
911
+ .format(obj.message.code, {
912
+ parser: obj.message.type === 'typescript' ? 'babel-ts' : 'babel',
913
+ printWidth: 120,
914
+ semi: true,
915
+ tabWidth: 4,
916
+ useTabs: false,
917
+ trailingComma: 'all',
918
+ singleQuote: true,
919
+ singleAttributePerLine: true,
920
+ endOfLine: 'lf',
921
+ bracketSpacing: true,
922
+ arrowParens: 'avoid',
923
+ quoteProps: 'as-needed',
924
+ })
925
+ .then(formattedCode => {
926
+ if (obj.callback) {
927
+ this.sendTo(obj.from, obj.command, { code: formattedCode }, obj.callback);
928
+ }
929
+ else {
930
+ this.logWithLineInfo(`Formatted code:\n${formattedCode}`);
931
+ }
932
+ })
933
+ .catch(e => {
934
+ this.logError('Prettier', 'Error formatting code:', e);
935
+ this.sendTo(obj.from, obj.command, { error: e.toString() }, obj.callback);
936
+ });
937
+ }
938
+ catch (e) {
939
+ this.logError('Prettier', 'Error formatting code:', e);
940
+ this.sendTo(obj.from, obj.command, { error: e.toString() }, obj.callback);
941
+ }
942
+ }
943
+ else {
944
+ this.sendTo(obj.from, obj.command, { error: 'No code provided' }, obj.callback);
945
+ }
946
+ break;
947
+ }
948
+ }
949
+ }
950
+ onLog(msg) {
951
+ Object.keys(this.logSubscriptions).forEach((name) => this.logSubscriptions[name].forEach(handler => {
952
+ if (typeof handler.cb === 'function' &&
953
+ (handler.severity === '*' || handler.severity === msg.severity)) {
954
+ handler.sandbox.logHandler = handler.severity || '*';
955
+ handler.cb.call(handler.sandbox, msg);
956
+ handler.sandbox.logHandler = undefined;
957
+ }
958
+ }));
959
+ }
960
+ logError(scriptName, msg, e, offs) {
961
+ const stack = e.stack ? e.stack.toString().split('\n') : e ? e.toString() : '';
962
+ if (!msg.includes('\n')) {
963
+ msg = msg.replace(/[: ]*$/, ': ');
964
+ }
965
+ if (!msg.endsWith(' ')) {
966
+ msg += ':';
967
+ }
968
+ if (!scriptName.startsWith(SCRIPT_CODE_MARKER)) {
969
+ scriptName = SCRIPT_CODE_MARKER + scriptName;
970
+ }
971
+ this.errorLogFunction.error(`${scriptName}: ${msg}${this.fixLineNo(stack[0])}`);
972
+ for (let i = offs || 1; i < stack.length; i++) {
973
+ if (!stack[i]) {
974
+ continue;
975
+ }
976
+ if (stack[i].match(/runInNewContext|javascript\.js:/)) {
977
+ break;
978
+ }
979
+ this.errorLogFunction.error(`${scriptName}: ${this.fixLineNo(stack[i])}`);
980
+ }
981
+ }
982
+ logWithLineInfo(msg) {
983
+ this.errorLogFunction.warn(msg);
984
+ // get current error stack
985
+ const stack = new Error().stack?.split('\n');
986
+ if (stack) {
987
+ for (let i = 3; i < stack.length; i++) {
988
+ if (!stack[i]) {
989
+ continue;
990
+ }
991
+ if (stack[i].match(/runInContext|runInNewContext|javascript\.js:/)) {
992
+ break;
993
+ }
994
+ this.errorLogFunction.warn(this.fixLineNo(stack[i]));
995
+ }
996
+ }
997
+ }
998
+ async main() {
999
+ // Patch the font as it sometimes is wrong
1000
+ if (!this.context.debugMode) {
1001
+ if (await this.patchFont()) {
1002
+ this.log.debug('Font patched');
1003
+ }
1004
+ }
1005
+ this.log.debug(`config.subscribe (Do not subscribe all states on start): ${this.config.subscribe}`);
1006
+ // correct jsonConfig for admin
1007
+ const instObj = await this.getForeignObjectAsync(`system.adapter.${this.namespace}`);
1008
+ if (instObj?.common) {
1009
+ if (instObj.common.adminUI?.config !== 'json') {
1010
+ if (instObj.common.adminUI) {
1011
+ instObj.common.adminUI.config = 'json';
1012
+ }
1013
+ else {
1014
+ instObj.common.adminUI = { config: 'json' };
1015
+ }
1016
+ void this.setForeignObject(instObj._id, instObj);
1017
+ }
1018
+ }
1019
+ if (webstormDebug) {
1020
+ this.errorLogFunction = {
1021
+ error: console.error,
1022
+ warn: console.warn,
1023
+ info: console.info,
1024
+ debug: console.log,
1025
+ silly: console.log,
1026
+ };
1027
+ this.context.errorLogFunction = this.errorLogFunction;
1028
+ }
1029
+ this.activeStr = `${this.namespace}.scriptEnabled.`;
1030
+ this.mods.fs = new protectFs_1.default(this.log, (0, adapter_core_1.getAbsoluteDefaultDataDir)());
1031
+ this.mods['fs/promises'] = this.mods.fs.promises; // to avoid require('fs/promises');
1032
+ // try to read TS declarations
1033
+ try {
1034
+ tsAmbient = {
1035
+ 'javascript.d.ts': (0, node_fs_1.readFileSync)(this.mods.path.join(__dirname, 'lib/javascript.d.ts'), 'utf8'),
1036
+ };
1037
+ this.tsServer.provideAmbientDeclarations(tsAmbient);
1038
+ jsDeclarationServer.provideAmbientDeclarations(tsAmbient);
1039
+ }
1040
+ catch (err) {
1041
+ this.log.warn(`Could not read TypeScript ambient declarations: ${err}`);
1042
+ // This should not happen, so send an error report to Sentry
1043
+ if (this.supportsFeature && this.supportsFeature('PLUGINS')) {
1044
+ const sentryInstance = this.getPluginInstance('sentry');
1045
+ if (sentryInstance) {
1046
+ const sentryObject = sentryInstance.getSentryObject();
1047
+ sentryObject?.captureException(err);
1048
+ }
1049
+ }
1050
+ // Keep the adapter from crashing when the included typings cannot be read
1051
+ tsAmbient = {};
1052
+ }
1053
+ await this.installLibraries();
1054
+ // Load the TS declarations for Node.js and all 3rd party modules
1055
+ this.loadTypeScriptDeclarations();
1056
+ await this.getData();
1057
+ this.context.scheduler = new scheduler_1.Scheduler(this.log, Date, this.mods.suncalc, this.config.latitude, this.config.longitude);
1058
+ await this.dayTimeSchedules();
1059
+ await this.sunTimeSchedules();
1060
+ await this.timeSchedule();
1061
+ // Warning. It could have a side effect in compact mode, so all adapters will accept self-signed certificates
1062
+ if (this.config.allowSelfSignedCerts) {
1063
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
1064
+ }
1065
+ const doc = await this.getObjectViewAsync('script', 'javascript', {});
1066
+ if (doc?.rows?.length) {
1067
+ // assemble global script
1068
+ for (let g = 0; g < doc.rows.length; g++) {
1069
+ const obj = doc.rows[g].value;
1070
+ if (checkIsGlobal(obj)) {
1071
+ if (obj && obj.common) {
1072
+ const engineType = (obj.common.engineType || '').toLowerCase();
1073
+ if (obj.common.enabled) {
1074
+ if (engineType.startsWith('typescript')) {
1075
+ // TypeScript
1076
+ this.log.info(`${obj._id}: compiling TypeScript source...`);
1077
+ // In order to compile global TypeScript, we need to do some transformations
1078
+ // 1. For top-level-await, some statements must be wrapped in an immediately-invoked async function
1079
+ // 2. If any global script uses `import`, the declarations are no longer visible if they are not exported with `declare global`
1080
+ const transformedSource = (0, typescriptTools_1.transformScriptBeforeCompilation)(obj.common.source, true);
1081
+ // The source code must be transformed in order to support top level await
1082
+ // Global scripts must not be treated as a module, otherwise their methods
1083
+ // cannot be found by the normal scripts
1084
+ // We need to hash both global declarations that are known until now
1085
+ // AND the script source, because changing either can change the compilation output
1086
+ const sourceHash = (0, tools_1.hashSource)(tsSourceHashBase + this.globalDeclarations + transformedSource);
1087
+ let compiled;
1088
+ let declarations;
1089
+ // If we already stored the compiled source code and the original source hash,
1090
+ // use the hash to check whether we can rely on the compiled source code or
1091
+ // if we need to compile it again
1092
+ if (typeof obj.common.compiled === 'string' &&
1093
+ typeof obj.common.sourceHash === 'string' &&
1094
+ sourceHash === obj.common.sourceHash) {
1095
+ // We can reuse the stored source
1096
+ compiled = obj.common.compiled;
1097
+ declarations = obj.common.declarations;
1098
+ this.log.info(`${obj._id}: source code did not change, using cached compilation result...`);
1099
+ }
1100
+ else {
1101
+ // We don't have a hashed source code, or the original source changed, compile it
1102
+ const filename = (0, typescriptTools_1.scriptIdToTSFilename)(obj._id);
1103
+ let tsCompiled;
1104
+ try {
1105
+ tsCompiled = this.tsServer.compile(filename, transformedSource);
1106
+ }
1107
+ catch (err) {
1108
+ this.log.error(`${obj._id}: TypeScript compilation failed:\n${err}`);
1109
+ continue;
1110
+ }
1111
+ const errors = tsCompiled.diagnostics
1112
+ .map(diag => `${diag.annotatedSource}\n`)
1113
+ .join('\n');
1114
+ if (tsCompiled.success) {
1115
+ if (errors.length > 0) {
1116
+ this.log.warn(`${obj._id}: TypeScript compilation completed with errors:\n${errors}`);
1117
+ }
1118
+ else {
1119
+ this.log.info(`${obj._id}: TypeScript compilation successful`);
1120
+ }
1121
+ compiled = tsCompiled.result;
1122
+ // Global scripts that have been transformed to support `import` need to have their declarations transformed aswell
1123
+ declarations = (0, typescriptTools_1.transformGlobalDeclarations)(tsCompiled.declarations || '');
1124
+ const newCommon = {
1125
+ sourceHash,
1126
+ compiled,
1127
+ };
1128
+ if (declarations) {
1129
+ newCommon.declarations = declarations;
1130
+ }
1131
+ // Store the compiled source and the original source hash, so we don't need to do the work again next time
1132
+ this.ignoreObjectChange.add(obj._id); // ignore the next change and don't restart scripts
1133
+ void this.extendForeignObject(obj._id, {
1134
+ common: newCommon,
1135
+ });
1136
+ }
1137
+ else {
1138
+ this.log.error(`${obj._id}: TypeScript compilation failed:\n${errors}`);
1139
+ continue;
1140
+ }
1141
+ }
1142
+ this.globalScript += `${compiled}\n`;
1143
+ // if declarations were generated, remember them
1144
+ if (declarations != null) {
1145
+ this.provideDeclarationsForGlobalScript(obj._id, declarations);
1146
+ }
1147
+ }
1148
+ else {
1149
+ // javascript
1150
+ const sourceCode = obj.common.source;
1151
+ this.globalScript += `${sourceCode}\n`;
1152
+ // try to compile the declarations so TypeScripts can use
1153
+ // functions defined in global JavaScripts
1154
+ const filename = (0, typescriptTools_1.scriptIdToTSFilename)(obj._id);
1155
+ let tsCompiled;
1156
+ try {
1157
+ tsCompiled = jsDeclarationServer.compile(filename, sourceCode);
1158
+ }
1159
+ catch (err) {
1160
+ this.log.warn(`${obj._id}: Error while generating type declarations, skipping:\n${err}`);
1161
+ continue;
1162
+ }
1163
+ // if declarations were generated, remember them
1164
+ if (tsCompiled.success && tsCompiled.declarations != null) {
1165
+ this.provideDeclarationsForGlobalScript(obj._id, tsCompiled.declarations);
1166
+ }
1167
+ }
1168
+ }
1169
+ }
1170
+ }
1171
+ }
1172
+ }
1173
+ this.globalScript = this.globalScript.replace(/\r\n/g, '\n');
1174
+ this.globalScriptLines = this.globalScript.split(/\n/g).length - 1;
1175
+ if (doc?.rows?.length) {
1176
+ // load all scripts
1177
+ for (let i = 0; i < doc.rows.length; i++) {
1178
+ if (!checkIsGlobal(doc.rows[i].value)) {
1179
+ void this.loadScript(doc.rows[i].value);
1180
+ }
1181
+ }
1182
+ }
1183
+ if (this.config.mirrorPath) {
1184
+ this.config.mirrorInstance = parseInt(this.config.mirrorInstance, 10) || 0;
1185
+ if (this.instance === this.config.mirrorInstance) {
1186
+ const ioBDataDir = (0, adapter_core_1.getAbsoluteDefaultDataDir)() + node_path_1.sep;
1187
+ this.config.mirrorPath = (0, node_path_1.normalize)(this.config.mirrorPath);
1188
+ let mirrorForbidden = false;
1189
+ for (let dir of forbiddenMirrorLocations) {
1190
+ dir = (0, node_path_1.join)(ioBDataDir, dir) + node_path_1.sep;
1191
+ if (dir.includes(this.config.mirrorPath) || this.config.mirrorPath.startsWith(dir)) {
1192
+ this.log.error(`The Mirror directory is not allowed to be a central ioBroker directory!`);
1193
+ this.log.error(`Directory ${this.config.mirrorPath} is not allowed to mirror files!`);
1194
+ mirrorForbidden = true;
1195
+ break;
1196
+ }
1197
+ }
1198
+ if (!mirrorForbidden) {
1199
+ this.mirror = new mirror_1.Mirror({
1200
+ adapter: this,
1201
+ log: this.log,
1202
+ diskRoot: this.config.mirrorPath,
1203
+ });
1204
+ }
1205
+ }
1206
+ }
1207
+ // CHeck setState counter per minute and stop script if too high
1208
+ this.setStateCountCheckInterval = setInterval(() => {
1209
+ Object.keys(this.scripts).forEach(id => {
1210
+ if (!this.scripts[id]) {
1211
+ return;
1212
+ }
1213
+ const currentSetStatePerMinuteCounter = this.scripts[id].setStatePerMinuteCounter;
1214
+ this.scripts[id].setStatePerMinuteCounter = 0;
1215
+ if (currentSetStatePerMinuteCounter > this.config.maxSetStatePerMinute) {
1216
+ this.scripts[id].setStatePerMinuteProblemCounter++;
1217
+ this.log.debug(`${id}: Script has reached the maximum of ${this.config.maxSetStatePerMinute} setState calls per minute in ${this.scripts[id].setStatePerMinuteProblemCounter} consecutive minutes`);
1218
+ // Allow "too high counters" for 1 minute for script starts or such and only
1219
+ // stop the script when lasts longer
1220
+ if (this.scripts[id].setStatePerMinuteProblemCounter > 1) {
1221
+ this.log.error(`${id}: Script is calling setState more than ${this.config.maxSetStatePerMinute} times per minute! Stopping Script now! Please check your script!`);
1222
+ void this.stopScript(id);
1223
+ }
1224
+ }
1225
+ else if (this.scripts[id].setStatePerMinuteProblemCounter > 0) {
1226
+ this.scripts[id].setStatePerMinuteProblemCounter--;
1227
+ 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}`);
1228
+ }
1229
+ });
1230
+ }, 60000);
1231
+ }
1232
+ loadTypeScriptDeclarations() {
1233
+ // try to load the typings on disk for all 3rd party modules
1234
+ const packages = [
1235
+ 'node', // this provides auto-completion for most builtins
1236
+ '@iobroker/types', // this provides auto-completion for most builtins
1237
+ ];
1238
+ // Also include user-selected libraries (but only those that are also installed)
1239
+ if (typeof this.config?.libraries === 'string' && typeof this.config.libraryTypings === 'string') {
1240
+ const installedLibs = this.config.libraries
1241
+ .split(/[,;\s]+/)
1242
+ .map(s => s.trim().split('@')[0])
1243
+ .filter(s => !!s);
1244
+ const wantsTypings = this.config.libraryTypings
1245
+ .split(/[,;\s]+/)
1246
+ .map(s => s.trim())
1247
+ .filter(s => !!s);
1248
+ // Add all installed libraries the user has requested typings for to the list of packages
1249
+ for (const lib of installedLibs) {
1250
+ if (wantsTypings.includes(lib) && !packages.includes(lib)) {
1251
+ packages.push(lib);
1252
+ }
1253
+ }
1254
+ // Some packages have submodules (e.g., rxjs/operators) that are not exposed through the main entry point
1255
+ // If typings are requested for them, also add them if the base module is installed
1256
+ for (const lib of wantsTypings) {
1257
+ // Extract the package name and check if we need to add it
1258
+ if (!lib.includes('/')) {
1259
+ continue;
1260
+ }
1261
+ const pkgName = lib.substring(0, lib.indexOf('/'));
1262
+ if (installedLibs.includes(pkgName) && !packages.includes(lib)) {
1263
+ packages.push(lib);
1264
+ }
1265
+ }
1266
+ }
1267
+ for (const pkg of packages) {
1268
+ let pkgTypings = (0, typescriptTools_1.resolveTypings)(pkg, this.getAdapterScopedPackageIdentifier ? this.getAdapterScopedPackageIdentifier(pkg) : pkg,
1269
+ // node needs ambient typings, so we don't wrap it in declare module
1270
+ pkg !== 'node');
1271
+ if (!pkgTypings) {
1272
+ // Create the empty dummy declarations so users don't get the "not found" error
1273
+ // for installed packages
1274
+ if (pkg.includes('/')) {
1275
+ pkgTypings = {
1276
+ [`node_modules/${pkg}/index.d.ts`]: `declare module "${pkg}";`,
1277
+ };
1278
+ }
1279
+ else {
1280
+ pkgTypings = {
1281
+ [`node_modules/@types/${pkg}/index.d.ts`]: `declare module "${pkg}";`,
1282
+ };
1283
+ }
1284
+ }
1285
+ this.log.debug(`Loaded TypeScript definitions for "${pkg}": ${JSON.stringify(Object.keys(pkgTypings))}`);
1286
+ // remember the declarations for the editor
1287
+ Object.assign(tsAmbient, pkgTypings);
1288
+ // and give the language servers access to them
1289
+ this.tsServer.provideAmbientDeclarations(pkgTypings);
1290
+ jsDeclarationServer.provideAmbientDeclarations(pkgTypings);
1291
+ }
1292
+ }
1293
+ updateObjectContext(id, obj) {
1294
+ if (obj) {
1295
+ // add state to state ID's list
1296
+ if (obj.type === 'state') {
1297
+ if (!this.stateIds.includes(id)) {
1298
+ this.stateIds.push(id);
1299
+ this.stateIds.sort();
1300
+ }
1301
+ if (this.context.devices && this.context.channels) {
1302
+ const parts = id.split('.');
1303
+ parts.pop();
1304
+ const chn = parts.join('.');
1305
+ this.context.channels[chn] = this.context.channels[chn] || [];
1306
+ this.context.channels[chn].push(id);
1307
+ parts.pop();
1308
+ const dev = parts.join('.');
1309
+ this.context.devices[dev] = this.context.devices[dev] || [];
1310
+ this.context.devices[dev].push(id);
1311
+ }
1312
+ }
1313
+ }
1314
+ else {
1315
+ // delete object from state ID's list
1316
+ const pos = this.stateIds.indexOf(id);
1317
+ if (pos !== -1) {
1318
+ this.stateIds.splice(pos, 1);
1319
+ }
1320
+ if (this.context.devices && this.context.channels) {
1321
+ const parts = id.split('.');
1322
+ parts.pop();
1323
+ const chn = parts.join('.');
1324
+ if (this.context.channels[chn]) {
1325
+ const posChn = this.context.channels[chn].indexOf(id);
1326
+ posChn !== -1 && this.context.channels[chn].splice(posChn, 1);
1327
+ }
1328
+ parts.pop();
1329
+ const dev = parts.join('.');
1330
+ if (this.context.devices[dev]) {
1331
+ const posDev = this.context.devices[dev].indexOf(id);
1332
+ posDev !== -1 && this.context.devices[dev].splice(posDev, 1);
1333
+ }
1334
+ }
1335
+ delete this.folderCreationVerifiedObjects[id];
1336
+ }
1337
+ if (!obj && this.objects[id]) {
1338
+ // objects was deleted
1339
+ this.removeFromNames(id);
1340
+ delete this.objects[id];
1341
+ }
1342
+ else if (obj && !this.objects[id]) {
1343
+ // object was added
1344
+ this.objects[id] = obj;
1345
+ this.addToNames(obj);
1346
+ }
1347
+ else if (obj && this.objects[id].common) {
1348
+ // Object just changed
1349
+ this.objects[id] = obj;
1350
+ const n = this.getName(id);
1351
+ let nn = this.objects[id].common ? this.objects[id].common.name : '';
1352
+ if (nn && typeof nn === 'object') {
1353
+ nn = nn[(0, words_1.getLanguage)()] || nn.en;
1354
+ }
1355
+ if (n !== nn) {
1356
+ if (n) {
1357
+ this.removeFromNames(id);
1358
+ }
1359
+ if (nn) {
1360
+ this.addToNames(obj);
1361
+ }
1362
+ }
1363
+ }
1364
+ }
1365
+ async stopAllScripts() {
1366
+ const scripts = Object.keys(this.scripts);
1367
+ const promises = [];
1368
+ for (let i = 0; i < scripts.length; i++) {
1369
+ promises.push(this.stopScript(scripts[i]));
1370
+ }
1371
+ return Promise.all(promises).then(() => { });
1372
+ }
1373
+ convertBackStringifiedValues(id, state) {
1374
+ if (state &&
1375
+ typeof state.val === 'string' &&
1376
+ this.objects[id]?.common &&
1377
+ (this.objects[id].common.type === 'array' || this.objects[id].common.type === 'object')) {
1378
+ try {
1379
+ state.val = JSON.parse(state.val);
1380
+ }
1381
+ catch (err) {
1382
+ if (id.startsWith('javascript.') || id.startsWith('0_userdata.0')) {
1383
+ this.log.info(`Could not parse value for id "${id}" into ${this.objects[id].common.type}: ${err.toString()}`);
1384
+ }
1385
+ else {
1386
+ this.log.debug(`Could not parse value for id "${id}" into ${this.objects[id].common.type}: ${err.toString()}`);
1387
+ }
1388
+ }
1389
+ }
1390
+ return state;
1391
+ }
1392
+ prepareStateObjectSimple(id, state, isAck) {
1393
+ // otherwise, assume that the given state is the value to be set
1394
+ const oState = { val: state, ack: isAck };
1395
+ return this.prepareStateObject(id, oState);
1396
+ }
1397
+ prepareStateObject(id, state) {
1398
+ let oState;
1399
+ if (state && typeof state === 'object') {
1400
+ oState = state;
1401
+ }
1402
+ else {
1403
+ oState = { val: null };
1404
+ }
1405
+ if (this.config.subscribe) {
1406
+ return oState;
1407
+ }
1408
+ // set other values to have a full state object
1409
+ // mirrors logic from statesInRedis
1410
+ if (oState.ts === undefined) {
1411
+ oState.ts = Date.now();
1412
+ }
1413
+ if (oState.q === undefined) {
1414
+ oState.q = 0;
1415
+ }
1416
+ oState.from =
1417
+ typeof oState.from === 'string' && oState.from !== '' ? oState.from : `system.adapter.${this.namespace}`;
1418
+ if (oState.lc === undefined) {
1419
+ const formerStateValue = this.interimStateValues[id] || this.states[id];
1420
+ if (!formerStateValue) {
1421
+ oState.lc = oState.ts;
1422
+ }
1423
+ else {
1424
+ // isDeepStrictEqual works on objects and primitive values
1425
+ const hasChanged = !(0, node_util_1.isDeepStrictEqual)(formerStateValue.val, oState.val);
1426
+ if (!formerStateValue.lc || hasChanged) {
1427
+ oState.lc = oState.ts;
1428
+ }
1429
+ else {
1430
+ oState.lc = formerStateValue.lc;
1431
+ }
1432
+ }
1433
+ }
1434
+ return oState;
1435
+ }
1436
+ async getData() {
1437
+ await this.subscribeForeignObjectsAsync('*');
1438
+ if (!this.config.subscribe) {
1439
+ await this.subscribeForeignStatesAsync('*');
1440
+ }
1441
+ else {
1442
+ await this.subscribeStatesAsync('debug.to');
1443
+ await this.subscribeStatesAsync('scriptEnabled.*');
1444
+ }
1445
+ this.log.info('requesting all states');
1446
+ const statesPromise = this.getForeignStatesAsync('*')
1447
+ .then(res => {
1448
+ if (!res) {
1449
+ this.log.error(`Could not initialize states: no result`);
1450
+ this.terminate(adapter_core_1.EXIT_CODES.START_IMMEDIATELY_AFTER_STOP);
1451
+ return;
1452
+ }
1453
+ if (!this.config.subscribe) {
1454
+ this.states = Object.assign(res, this.states);
1455
+ this.context.states = this.states;
1456
+ this.addGetProperty(this.states);
1457
+ }
1458
+ // remember all IDs
1459
+ for (const id in res) {
1460
+ if (Object.prototype.hasOwnProperty.call(res, id)) {
1461
+ this.stateIds.push(id);
1462
+ }
1463
+ }
1464
+ this.statesInitDone = true;
1465
+ this.log.info('received all states');
1466
+ })
1467
+ .catch((err) => {
1468
+ this.log.error(`Could not initialize states: ${err?.message || 'no result'}`);
1469
+ this.terminate(adapter_core_1.EXIT_CODES.START_IMMEDIATELY_AFTER_STOP);
1470
+ });
1471
+ this.log.info('requesting all objects');
1472
+ const objectsPromise = this.getObjectListAsync({ include_docs: true })
1473
+ .then(res => {
1474
+ if (!res?.rows) {
1475
+ this.log.error(`Could not initialize objects: no result`);
1476
+ this.terminate(adapter_core_1.EXIT_CODES.START_IMMEDIATELY_AFTER_STOP);
1477
+ return;
1478
+ }
1479
+ this.objects = {};
1480
+ this.context.objects = this.objects;
1481
+ for (let i = 0; i < res.rows.length; i++) {
1482
+ if (!res.rows[i].doc) {
1483
+ this.log.debug(`Got empty object for index ${i} (${res.rows[i].id})`);
1484
+ continue;
1485
+ }
1486
+ if (this.objects[res.rows[i].doc._id] === undefined) {
1487
+ // If was already there ignore
1488
+ this.objects[res.rows[i].doc._id] = res.rows[i].doc;
1489
+ }
1490
+ this.objects[res.rows[i].doc._id].type === 'enum' && this._enums.push(res.rows[i].doc._id);
1491
+ // Collect all names
1492
+ this.addToNames(this.objects[res.rows[i].doc._id]);
1493
+ }
1494
+ this.addGetProperty(this.objects);
1495
+ const systemConfig = this.objects['system.config'];
1496
+ // set language for debug messages
1497
+ if (systemConfig?.common?.language) {
1498
+ (0, words_1.setLanguage)(systemConfig.common.language);
1499
+ this.language = systemConfig.common.language;
1500
+ this.context.language = this.language;
1501
+ }
1502
+ else if (this.language) {
1503
+ (0, words_1.setLanguage)(this.language);
1504
+ this.context.language = this.language;
1505
+ }
1506
+ // try to use system coordinates
1507
+ if (this.config.useSystemGPS) {
1508
+ if (systemConfig?.common?.latitude || systemConfig?.common?.longitude) {
1509
+ this.config.latitude = systemConfig.common.latitude;
1510
+ this.config.longitude = systemConfig.common.longitude;
1511
+ }
1512
+ else if (this.latitude && this.longitude) {
1513
+ this.config.latitude = this.latitude;
1514
+ this.config.longitude = this.longitude;
1515
+ }
1516
+ }
1517
+ this.config.latitude = parseFloat(this.config.latitude);
1518
+ this.config.longitude = parseFloat(this.config.longitude);
1519
+ if (isNaN(this.config.latitude)) {
1520
+ this.log.warn(`Configured latitude is not a number - check (instance/system) configuration`);
1521
+ }
1522
+ else if (this.config.latitude < -90 || this.config.latitude > 90) {
1523
+ this.log.warn(`Configured latitude "${this.config.latitude}" is invalid - check (instance/system) configuration`);
1524
+ }
1525
+ if (isNaN(this.config.longitude)) {
1526
+ this.log.warn(`Configured longitude is not a number - check (instance/system) configuration`);
1527
+ }
1528
+ else if (this.config.longitude < -180 || this.config.longitude > 180) {
1529
+ this.log.warn(`Configured longitude "${this.config.longitude}" is invalid - check (instance/system) configuration`);
1530
+ }
1531
+ this.config.sunriseEvent = this.config.sunriseEvent || 'nightEnd';
1532
+ this.config.sunriseOffset = this.config.sunriseOffset || 0;
1533
+ this.config.sunriseLimitStart = this.config.sunriseLimitStart || '06:00';
1534
+ this.config.sunriseLimitEnd = this.config.sunriseLimitEnd || '12:00';
1535
+ this.config.sunsetEvent = this.config.sunsetEvent || 'dusk';
1536
+ this.config.sunsetOffset = this.config.sunsetOffset || 0;
1537
+ this.config.sunsetLimitStart = this.config.sunsetLimitStart || '18:00';
1538
+ this.config.sunsetLimitEnd = this.config.sunsetLimitEnd || '23:00';
1539
+ this.objectsInitDone = true;
1540
+ this.log.info('received all objects');
1541
+ })
1542
+ .catch((err) => {
1543
+ this.log.error(`Could not initialize objects: ${err?.message || 'no result'}`);
1544
+ this.terminate(adapter_core_1.EXIT_CODES.START_IMMEDIATELY_AFTER_STOP);
1545
+ });
1546
+ return Promise.all([statesPromise, objectsPromise]).then(() => { });
1547
+ }
1548
+ async createActiveObject(id, enabled) {
1549
+ const idActive = `${this.namespace}.scriptEnabled.${id.substring(SCRIPT_CODE_MARKER.length)}`;
1550
+ if (!this.objects[idActive]) {
1551
+ this.objects[idActive] = {
1552
+ _id: idActive,
1553
+ common: {
1554
+ name: `scriptEnabled.${id.substring(SCRIPT_CODE_MARKER.length)}`,
1555
+ desc: 'controls script activity',
1556
+ type: 'boolean',
1557
+ write: true,
1558
+ read: true,
1559
+ role: 'switch.active',
1560
+ },
1561
+ native: {
1562
+ script: id,
1563
+ },
1564
+ type: 'state',
1565
+ };
1566
+ try {
1567
+ await this.setForeignObjectAsync(idActive, this.objects[idActive]);
1568
+ const intermediateStateValue = this.prepareStateObjectSimple(idActive, enabled, true);
1569
+ await this.setForeignStateAsync(idActive, enabled, true);
1570
+ if (enabled && !this.config.subscribe) {
1571
+ this.interimStateValues[idActive] = intermediateStateValue;
1572
+ }
1573
+ }
1574
+ catch {
1575
+ // ignore
1576
+ }
1577
+ }
1578
+ else {
1579
+ const state = await this.getForeignStateAsync(idActive);
1580
+ if (state && state.val !== enabled) {
1581
+ const intermediateStateValue = this.prepareStateObjectSimple(idActive, enabled, true);
1582
+ await this.setForeignStateAsync(idActive, enabled, true);
1583
+ if (enabled && !this.config.subscribe) {
1584
+ this.interimStateValues[id] = intermediateStateValue;
1585
+ }
1586
+ }
1587
+ }
1588
+ }
1589
+ async createProblemObject(id) {
1590
+ const idProblem = `${this.namespace}.scriptProblem.${id.substring(SCRIPT_CODE_MARKER.length)}`;
1591
+ if (!this.objects[idProblem]) {
1592
+ this.objects[idProblem] = {
1593
+ _id: idProblem,
1594
+ common: {
1595
+ name: `scriptProblem.${id.substring(SCRIPT_CODE_MARKER.length)}`,
1596
+ desc: 'Script has a problem',
1597
+ type: 'boolean',
1598
+ expert: true,
1599
+ write: false,
1600
+ read: true,
1601
+ role: 'indicator.error',
1602
+ },
1603
+ native: {
1604
+ script: id,
1605
+ },
1606
+ type: 'state',
1607
+ };
1608
+ try {
1609
+ await this.setForeignObjectAsync(idProblem, this.objects[idProblem]);
1610
+ await this.setForeignStateAsync(idProblem, false, true);
1611
+ }
1612
+ catch {
1613
+ // ignore
1614
+ }
1615
+ }
1616
+ else {
1617
+ const state = await this.getForeignStateAsync(idProblem);
1618
+ if (state && state.val !== false) {
1619
+ await this.setForeignStateAsync(idProblem, false, true);
1620
+ }
1621
+ }
1622
+ }
1623
+ addToNames(obj) {
1624
+ const id = obj._id;
1625
+ if (obj.common?.name) {
1626
+ let name = obj.common.name;
1627
+ if (name && typeof name === 'object') {
1628
+ name = name[(0, words_1.getLanguage)()] || name.en;
1629
+ }
1630
+ if (!name || typeof name !== 'string') {
1631
+ // TODO, take name in current language
1632
+ return;
1633
+ }
1634
+ if (!this.names[name]) {
1635
+ this.names[name] = id;
1636
+ }
1637
+ else {
1638
+ // convert to array
1639
+ if (!Array.isArray(this.names[name])) {
1640
+ this.names[name] = [this.names[name]];
1641
+ }
1642
+ this.names[name].push(id);
1643
+ }
1644
+ }
1645
+ }
1646
+ removeFromNames(id) {
1647
+ const n = this.getName(id);
1648
+ if (n) {
1649
+ if (Array.isArray(this.names[n])) {
1650
+ const pos = this.names[n].indexOf(id);
1651
+ if (pos > -1) {
1652
+ this.names[n].splice(pos, 1);
1653
+ if (this.names[n].length === 1) {
1654
+ this.names[n] = this.names[n][0];
1655
+ }
1656
+ }
1657
+ }
1658
+ else {
1659
+ delete this.names[n];
1660
+ }
1661
+ }
1662
+ }
1663
+ getName(id) {
1664
+ for (const n in this.names) {
1665
+ if (this.names[n] && Array.isArray(this.names[n])) {
1666
+ if (this.names[n].includes(id)) {
1667
+ return n;
1668
+ }
1669
+ }
1670
+ else if (this.names[n] === id) {
1671
+ return n;
1672
+ }
1673
+ }
1674
+ return null;
1675
+ }
1676
+ async installNpm(npmLib) {
1677
+ return new Promise((resolve, reject) => {
1678
+ const path = __dirname;
1679
+ // Also, set the working directory (cwd) of the process instead of using --prefix
1680
+ // because that has ugly bugs on Windows
1681
+ const cmd = `npm install ${npmLib} --omit=dev`;
1682
+ this.log.info(`Installing ${npmLib} into ${__dirname} - cmd: ${cmd}`);
1683
+ // System call used for update of js-controller itself,
1684
+ // because during the installation the npm packet will be deleted too, but some files must be loaded even during the installation process.
1685
+ const child = this.mods.child_process.exec(cmd, {
1686
+ windowsHide: true,
1687
+ cwd: path,
1688
+ });
1689
+ child.stdout?.on('data', buf => this.log.info(buf.toString('utf8')));
1690
+ child.stderr?.on('data', buf => this.log.error(buf.toString('utf8')));
1691
+ child.on('err', err => {
1692
+ this.log.error(`Cannot install ${npmLib}: ${err}`);
1693
+ reject(new Error(`Cannot install ${npmLib}: ${err}`));
1694
+ });
1695
+ child.on('error', err => {
1696
+ this.log.error(`Cannot install ${npmLib}: ${err}`);
1697
+ reject(new Error(`Cannot install ${npmLib}: ${err}`));
1698
+ });
1699
+ child.on('exit', (code /* , signal */) => {
1700
+ if (code) {
1701
+ this.log.error(`Cannot install ${npmLib}: ${code}`);
1702
+ reject(new Error(`Cannot install ${npmLib}: ${code}`));
1703
+ }
1704
+ // command succeeded
1705
+ resolve(code);
1706
+ });
1707
+ });
1708
+ }
1709
+ async installLibraries() {
1710
+ if (typeof this.config?.libraries !== 'string') {
1711
+ this.config.libraries = '';
1712
+ }
1713
+ const libraries = this.config.libraries
1714
+ .split(/[,;\s]+/)
1715
+ .map(d => d.trim())
1716
+ .filter(d => d);
1717
+ this.log.debug(`Custom libraries in config: "${this.config.libraries}": ${JSON.stringify(libraries)}`);
1718
+ let installedNodeModules = [];
1719
+ const keepModules = [];
1720
+ // js-controller >= 6.x
1721
+ if (typeof this.listInstalledNodeModules === 'function') {
1722
+ installedNodeModules = await this.listInstalledNodeModules();
1723
+ this.log.debug(`Found installed libraries: ${JSON.stringify(installedNodeModules)}`);
1724
+ }
1725
+ for (const lib of libraries) {
1726
+ let depName = lib;
1727
+ let version = 'latest';
1728
+ if (depName.includes('@') && depName.lastIndexOf('@') > 0) {
1729
+ const parts = depName.split('@');
1730
+ version = parts.pop() ?? 'latest';
1731
+ depName = parts.join('@');
1732
+ }
1733
+ /** The real module name, because the dependency can be an url too */
1734
+ let moduleName = depName;
1735
+ if (URL.canParse(depName)) {
1736
+ moduleName = await (0, nodeModulesManagement_1.requestModuleNameByUrl)(depName);
1737
+ this.log.debug(`Found custom library in config: "${moduleName}@${version}" (from ${depName})`);
1738
+ }
1739
+ else {
1740
+ this.log.debug(`Found custom library in config: "${moduleName}@${version}"`);
1741
+ }
1742
+ keepModules.push(moduleName);
1743
+ // js-controller >= 6.x
1744
+ if (typeof this.installNodeModule === 'function') {
1745
+ try {
1746
+ const result = await this.installNodeModule(depName, { version });
1747
+ if (result.success) {
1748
+ this.log.debug(`Installed custom library: "${moduleName}@${version}"`);
1749
+ const importedModule = await this.importNodeModule(moduleName);
1750
+ this.mods[moduleName] = importedModule.default ?? importedModule;
1751
+ }
1752
+ else {
1753
+ this.log.warn(`Cannot install custom npm package "${moduleName}@${version}"`);
1754
+ }
1755
+ }
1756
+ catch (err) {
1757
+ this.log.warn(`Cannot install custom npm package "${moduleName}@${version}": ${err}`);
1758
+ }
1759
+ }
1760
+ else if (!(0, node_fs_1.existsSync)(`${__dirname}/node_modules/${depName}/package.json`)) {
1761
+ // js-controller < 6.x
1762
+ this.log.info(`Installing custom library (legacy mode): "${lib}"`);
1763
+ try {
1764
+ await this.installNpm(lib);
1765
+ this.log.info(`Installed custom npm package (legacy mode): "${lib}"`);
1766
+ }
1767
+ catch (err) {
1768
+ this.log.warn(`Cannot install custom npm package "${lib}" (legacy mode): ${err.toString()}`);
1769
+ }
1770
+ }
1771
+ }
1772
+ // js-controller >= 6.x
1773
+ if (typeof this.uninstallNodeModule === 'function') {
1774
+ for (const installedNodeModule of installedNodeModules) {
1775
+ if (!keepModules.includes(installedNodeModule)) {
1776
+ try {
1777
+ await this.uninstallNodeModule(installedNodeModule);
1778
+ this.log.info(`Removed custom npm package: "${installedNodeModule}"`);
1779
+ }
1780
+ catch (err) {
1781
+ this.log.warn(`Cannot remove custom npm package ${installedNodeModule}: ${err.toString()}`);
1782
+ }
1783
+ }
1784
+ }
1785
+ }
1786
+ }
1787
+ createVM(source, name, wrapAsync) {
1788
+ if (this.context.debugMode && name !== this.context.debugMode) {
1789
+ return false;
1790
+ }
1791
+ if (!this.context.debugMode) {
1792
+ const logSubscriptionsText = "\n;\nlog(`registered ${__engine.__subscriptions} subscription${__engine.__subscriptions === 1 ? '' : 's'}," +
1793
+ " ${__engine.__schedules} schedule${__engine.__schedules === 1 ? '' : 's'}," +
1794
+ " ${__engine.__subscriptionsMessage} message${__engine.__subscriptionsMessage === 1 ? '' : 's'}," +
1795
+ " ${__engine.__subscriptionsLog} log${__engine.__subscriptionsLog === 1 ? '' : 's'}" +
1796
+ " and ${__engine.__subscriptionsFile} file subscription${__engine.__subscriptionsFile === 1 ? '' : 's'}`);\n";
1797
+ if (wrapAsync) {
1798
+ source = `(async () => {\n${source}\n${logSubscriptionsText}\n})();`;
1799
+ }
1800
+ else {
1801
+ if (source.endsWith('export {};\n')) {
1802
+ // If the source ends with "export {};" place the log subscriptions before it
1803
+ source = source.slice(0, -11); // remove "export {};\n"
1804
+ source = `${source}\n${logSubscriptionsText}\nexport {};\n`;
1805
+ }
1806
+ else {
1807
+ source = `${source}\n${logSubscriptionsText}`;
1808
+ }
1809
+ }
1810
+ }
1811
+ else {
1812
+ if (wrapAsync) {
1813
+ source = `(async () => {debugger;\n${source}\n})();`;
1814
+ }
1815
+ else {
1816
+ source = `debugger;${source}`;
1817
+ }
1818
+ }
1819
+ try {
1820
+ const options = {
1821
+ filename: name,
1822
+ // displayErrors: true,
1823
+ // lineOffset: this.globalScriptLines
1824
+ };
1825
+ return {
1826
+ script: new node_vm_1.Script(source, options),
1827
+ };
1828
+ }
1829
+ catch (err) {
1830
+ this.logError(name, `compile failed at`, err);
1831
+ return false;
1832
+ }
1833
+ }
1834
+ execute(script, name, engineType, verbose, debug) {
1835
+ script.intervals = [];
1836
+ script.timeouts = [];
1837
+ script.schedules = [];
1838
+ script.wizards = [];
1839
+ script.name = name;
1840
+ script.engineType = engineType;
1841
+ script._id = Math.floor(Math.random() * 0xffffffff);
1842
+ script.subscribes = {};
1843
+ script.subscribesFile = {};
1844
+ script.setStatePerMinuteCounter = 0;
1845
+ script.setStatePerMinuteProblemCounter = 0;
1846
+ void this.setState(`scriptProblem.${name.substring(SCRIPT_CODE_MARKER.length)}`, {
1847
+ val: false,
1848
+ ack: true,
1849
+ expire: 1000,
1850
+ });
1851
+ const sandbox = (0, sandbox_1.sandBox)(script, name, verbose, debug, this.context);
1852
+ try {
1853
+ script.script.runInNewContext(sandbox, {
1854
+ filename: name,
1855
+ displayErrors: true,
1856
+ // lineOffset: this.globalScriptLines
1857
+ });
1858
+ }
1859
+ catch (err) {
1860
+ void this.setState(`scriptProblem.${name.substring(SCRIPT_CODE_MARKER.length)}`, {
1861
+ val: true,
1862
+ ack: true,
1863
+ c: 'execute',
1864
+ });
1865
+ this.logError(name, 'Error by run:', err);
1866
+ }
1867
+ }
1868
+ unsubscribe(id) {
1869
+ if (!id) {
1870
+ this.log.warn('unsubscribe: empty name');
1871
+ return;
1872
+ }
1873
+ if (Array.isArray(id)) {
1874
+ id.forEach(sub => unsubscribe(sub));
1875
+ return;
1876
+ }
1877
+ if (id.constructor && id.constructor.name === 'RegExp') {
1878
+ // adapter.log.warn('unsubscribe: todo - process regexp');
1879
+ return;
1880
+ }
1881
+ if (typeof id !== 'string') {
1882
+ this.log.error(`unsubscribe: invalid type of id - ${typeof id}`);
1883
+ return;
1884
+ }
1885
+ const parts = id.split('.');
1886
+ const _adapter = `system.adapter.${parts[0]}.${parts[1]}`;
1887
+ if (this.objects[_adapter]?.common?.subscribable) {
1888
+ const a = `${parts[0]}.${parts[1]}`;
1889
+ const alive = `system.adapter.${a}.alive`;
1890
+ if (this.adapterSubs[alive]) {
1891
+ const pos = this.adapterSubs[alive].indexOf(id);
1892
+ if (pos !== -1) {
1893
+ this.adapterSubs[alive].splice(pos, 1);
1894
+ }
1895
+ if (!this.adapterSubs[alive].length) {
1896
+ delete this.adapterSubs[alive];
1897
+ }
1898
+ }
1899
+ this.sendTo(a, 'unsubscribe', id);
1900
+ }
1901
+ }
1902
+ // Analyze if logs are still required or not
1903
+ updateLogSubscriptions() {
1904
+ let found = false;
1905
+ // go through all scripts and check if some script still requires logs
1906
+ Object.keys(this.logSubscriptions).forEach(scriptName => {
1907
+ if (!this.logSubscriptions?.[scriptName] || !this.logSubscriptions[scriptName].length) {
1908
+ delete this.logSubscriptions[scriptName];
1909
+ }
1910
+ else {
1911
+ found = true;
1912
+ }
1913
+ });
1914
+ if (this.requireLog) {
1915
+ if (found && !this.logSubscribed) {
1916
+ this.logSubscribed = true;
1917
+ void this.requireLog(this.logSubscribed);
1918
+ this.log.info(`Subscribed to log messages (found logSubscriptions)`);
1919
+ }
1920
+ else if (!found && this.logSubscribed) {
1921
+ this.logSubscribed = false;
1922
+ void this.requireLog(this.logSubscribed);
1923
+ this.log.info(`Unsubscribed from log messages (not found logSubscriptions)`);
1924
+ }
1925
+ }
1926
+ }
1927
+ async stopScript(name) {
1928
+ this.log.info(`${name}: Stopping script`);
1929
+ await this.setState(`scriptEnabled.${name.substring(SCRIPT_CODE_MARKER.length)}`, false, true);
1930
+ if (this.messageBusHandlers[name]) {
1931
+ delete this.messageBusHandlers[name];
1932
+ }
1933
+ if (this.tempDirectories[name]) {
1934
+ try {
1935
+ this.mods.fs.rmSync(this.tempDirectories[name], { recursive: true });
1936
+ this.log.debug(`${name}: Removed temp directory: ${this.tempDirectories[name]}`);
1937
+ }
1938
+ catch {
1939
+ this.log.warn(`${name}: Unable to remove temp directory: ${this.tempDirectories[name]}`);
1940
+ }
1941
+ delete this.tempDirectories[name];
1942
+ }
1943
+ if (this.logSubscriptions[name]) {
1944
+ delete this.logSubscriptions[name];
1945
+ this.updateLogSubscriptions();
1946
+ }
1947
+ if (this.scripts[name]) {
1948
+ // Remove from subscriptions
1949
+ this.context.isEnums = false;
1950
+ if (this.config.subscribe) {
1951
+ // check all subscribed IDs
1952
+ Object.keys(this.scripts[name].subscribes).forEach(id => {
1953
+ if (this.subscribedPatterns[id]) {
1954
+ this.subscribedPatterns[id] -= this.scripts[name].subscribes[id];
1955
+ if (this.subscribedPatterns[id] <= 0) {
1956
+ this.unsubscribeForeignStates(id);
1957
+ delete this.subscribedPatterns[id];
1958
+ if (this.states[id]) {
1959
+ delete this.states[id];
1960
+ }
1961
+ }
1962
+ }
1963
+ });
1964
+ }
1965
+ for (let i = this.subscriptions.length - 1; i >= 0; i--) {
1966
+ if (this.subscriptions[i].name === name) {
1967
+ const sub = this.subscriptions.splice(i, 1)[0];
1968
+ if (sub?.pattern.id) {
1969
+ this.unsubscribe(sub.pattern.id);
1970
+ }
1971
+ }
1972
+ else {
1973
+ if ((!this.context.isEnums && this.subscriptions[i].pattern.enumName) ||
1974
+ this.subscriptions[i].pattern.enumId) {
1975
+ this.context.isEnums = true;
1976
+ }
1977
+ }
1978
+ }
1979
+ // check all subscribed files
1980
+ Object.keys(this.scripts[name].subscribesFile).forEach(key => {
1981
+ if (this.subscribedPatternsFile[key]) {
1982
+ this.subscribedPatternsFile[key] -= this.scripts[name].subscribesFile[key];
1983
+ if (this.subscribedPatternsFile[key] <= 0) {
1984
+ const [id, file] = key.split('$%$');
1985
+ void this.unsubscribeForeignFiles(id, file);
1986
+ delete this.subscribedPatternsFile[key];
1987
+ }
1988
+ }
1989
+ });
1990
+ for (let i = this.subscriptionsFile.length - 1; i >= 0; i--) {
1991
+ if (this.subscriptionsFile[i].name === name) {
1992
+ this.subscriptionsFile.splice(i, 1);
1993
+ }
1994
+ }
1995
+ for (let i = this.subscriptionsObject.length - 1; i >= 0; i--) {
1996
+ if (this.subscriptionsObject[i].name === name) {
1997
+ const sub = this.subscriptionsObject.splice(i, 1)[0];
1998
+ if (sub) {
1999
+ this.unsubscribeForeignObjects(sub.pattern);
2000
+ }
2001
+ }
2002
+ }
2003
+ // Stop all timeouts
2004
+ for (let i = 0; i < this.scripts[name].timeouts.length; i++) {
2005
+ clearTimeout(this.scripts[name].timeouts[i]);
2006
+ }
2007
+ // Stop all intervals
2008
+ for (let i = 0; i < this.scripts[name].intervals.length; i++) {
2009
+ clearInterval(this.scripts[name].intervals[i]);
2010
+ }
2011
+ // Stop all scheduled jobs
2012
+ for (let i = 0; i < this.scripts[name].schedules.length; i++) {
2013
+ if (this.scripts[name].schedules[i]) {
2014
+ const _name = this.scripts[name].schedules[i].name;
2015
+ if (!this.mods.nodeSchedule.cancelJob(this.scripts[name].schedules[i])) {
2016
+ this.log.error(`${name}: Error by canceling scheduled job "${_name}"`);
2017
+ }
2018
+ }
2019
+ }
2020
+ // Stop all time wizards jobs
2021
+ if (this.context.scheduler) {
2022
+ for (let i = 0; i < this.scripts[name].wizards.length; i++) {
2023
+ if (this.scripts[name].wizards[i]) {
2024
+ this.context.scheduler.remove(this.scripts[name].wizards[i]);
2025
+ }
2026
+ }
2027
+ }
2028
+ // if callback for on stop
2029
+ if (typeof this.scripts[name].onStopCb === 'function') {
2030
+ this.scripts[name].onStopTimeout =
2031
+ parseInt(this.scripts[name].onStopTimeout, 10) || 1000;
2032
+ await new Promise(resolve => {
2033
+ let timeout = setTimeout(() => {
2034
+ if (timeout) {
2035
+ timeout = null;
2036
+ resolve(true);
2037
+ }
2038
+ }, this.scripts[name].onStopTimeout);
2039
+ try {
2040
+ this.scripts[name].onStopCb(() => {
2041
+ if (timeout) {
2042
+ clearTimeout(timeout);
2043
+ timeout = null;
2044
+ resolve(true);
2045
+ }
2046
+ });
2047
+ }
2048
+ catch (err) {
2049
+ this.log.error(`${name}: error in onStop callback: ${err}`);
2050
+ }
2051
+ });
2052
+ }
2053
+ delete this.scripts[name];
2054
+ return true;
2055
+ }
2056
+ return false;
2057
+ }
2058
+ async prepareScript(obj) {
2059
+ if (obj?.common?.enabled && this.debugState.scriptName === obj._id) {
2060
+ const id = obj._id;
2061
+ await this.debugStop();
2062
+ this.log.info(`${id}: Debugging was stopped, because started in normal mode`);
2063
+ return this.prepareScript(obj);
2064
+ }
2065
+ if (obj?.common?.source &&
2066
+ (obj.common.enabled || this.context.debugMode === obj._id) &&
2067
+ obj.common.engine === `system.adapter.${this.namespace}`) {
2068
+ const name = obj._id;
2069
+ const nameId = name.substring(SCRIPT_CODE_MARKER.length);
2070
+ if (!nameId.length || nameId.endsWith('.')) {
2071
+ this.log.error(`${name}: Script name "${name}" is invalid!`);
2072
+ return false;
2073
+ }
2074
+ const idActive = `scriptEnabled.${nameId}`;
2075
+ if (!this.config.subscribe) {
2076
+ this.interimStateValues[idActive] = this.prepareStateObjectSimple(`${this.namespace}.${idActive}`, true, true);
2077
+ }
2078
+ await this.setState(idActive, true, true);
2079
+ obj.common.engineType = obj.common.engineType || '';
2080
+ if (obj.common.engineType.toLowerCase().startsWith('javascript') ||
2081
+ obj.common.engineType === 'Blockly' ||
2082
+ obj.common.engineType === 'Rules') {
2083
+ // Javascript
2084
+ this.log.info(`${name}: start JavaScript (${obj.common.engineType})`);
2085
+ let sourceFn = name;
2086
+ if (webstormDebug) {
2087
+ const fn = name.replace(/^script\.js\./, '').replace(/\./g, '/');
2088
+ sourceFn = this.mods.path.join(webstormDebug, `${fn}.js`);
2089
+ }
2090
+ const createdScript = this.createVM(`${this.globalScript}\n${obj.common.source}`, sourceFn, true);
2091
+ if (!createdScript) {
2092
+ return false;
2093
+ }
2094
+ this.scripts[name] = createdScript;
2095
+ this.execute(this.scripts[name], sourceFn, obj.common.engineType, obj.common.verbose, obj.common.debug);
2096
+ return true;
2097
+ }
2098
+ if (obj.common.engineType.toLowerCase().startsWith('typescript')) {
2099
+ // TypeScript
2100
+ this.log.info(`${name}: Compiling TypeScript source`);
2101
+ // The source code must be transformed in order to support top level await
2102
+ // and to force TypeScript to compile the code as a module
2103
+ const transformedSource = (0, typescriptTools_1.transformScriptBeforeCompilation)(obj.common.source, false);
2104
+ // We need to hash both global declarations that are known until now
2105
+ // AND the script source, because changing either can change the compilation output
2106
+ const sourceHash = (0, tools_1.hashSource)(tsSourceHashBase + this.globalDeclarations + transformedSource);
2107
+ let compiled;
2108
+ // If we already stored the compiled source code and the original source hash,
2109
+ // use the hash to check whether we can rely on the compiled source code or
2110
+ // if we need to compile it again
2111
+ if (typeof obj.common.compiled === 'string' &&
2112
+ typeof obj.common.sourceHash === 'string' &&
2113
+ sourceHash === obj.common.sourceHash) {
2114
+ // We can reuse the stored source
2115
+ compiled = obj.common.compiled;
2116
+ this.log.info(`${name}: source code did not change, using cached compilation result...`);
2117
+ }
2118
+ else {
2119
+ // We don't have a hashed source code, or the original source changed, compile it
2120
+ const filename = (0, typescriptTools_1.scriptIdToTSFilename)(name);
2121
+ let tsCompiled;
2122
+ try {
2123
+ tsCompiled = this.tsServer.compile(filename, transformedSource);
2124
+ }
2125
+ catch (err) {
2126
+ this.log.error(`${obj._id}: TypeScript compilation failed:\n${err}`);
2127
+ return false;
2128
+ }
2129
+ const errors = tsCompiled.diagnostics.map(diag => `${diag.annotatedSource}\n`).join('\n');
2130
+ if (tsCompiled.success) {
2131
+ if (errors.length > 0) {
2132
+ this.log.warn(`${name}: TypeScript compilation had errors:\n${errors}`);
2133
+ }
2134
+ else {
2135
+ this.log.info(`${name}: TypeScript compilation successful`);
2136
+ }
2137
+ compiled = tsCompiled.result || '';
2138
+ // Store the compiled source and the original source hash, so we don't need to do the work again next time
2139
+ this.ignoreObjectChange.add(name); // ignore the next change and don't restart scripts
2140
+ await this.extendForeignObjectAsync(name, {
2141
+ common: {
2142
+ sourceHash,
2143
+ compiled,
2144
+ },
2145
+ });
2146
+ }
2147
+ else {
2148
+ this.log.error(`${name}: TypeScript compilation failed:\n${errors}`);
2149
+ return false;
2150
+ }
2151
+ }
2152
+ const createdScript = this.createVM(`${this.globalScript}\n${compiled}`, name, false);
2153
+ if (!createdScript) {
2154
+ return false;
2155
+ }
2156
+ this.scripts[name] = createdScript;
2157
+ this.execute(this.scripts[name], name, obj.common.engineType, obj.common.verbose, obj.common.debug);
2158
+ return true;
2159
+ }
2160
+ this.log.warn(`${obj._id}: Unknown engine type: ${obj.common.engineType}`);
2161
+ return false;
2162
+ }
2163
+ let _name;
2164
+ if (obj?._id) {
2165
+ _name = obj._id;
2166
+ const scriptIdName = _name.substring(SCRIPT_CODE_MARKER.length);
2167
+ if (!scriptIdName.length || scriptIdName.endsWith('.')) {
2168
+ this.log.error(`${obj._id}: Script name "${_name}" is invalid!`);
2169
+ return false;
2170
+ }
2171
+ await this.setState(`scriptEnabled.${scriptIdName}`, false, true);
2172
+ }
2173
+ if (!obj) {
2174
+ this.log.error('Invalid script');
2175
+ }
2176
+ return false;
2177
+ }
2178
+ async loadScriptById(id) {
2179
+ let obj;
2180
+ try {
2181
+ obj = (await this.getForeignObjectAsync(id));
2182
+ }
2183
+ catch (err) {
2184
+ this.log.error(`${id}: Invalid script: ${err}`);
2185
+ }
2186
+ if (!obj) {
2187
+ return false;
2188
+ }
2189
+ return this.loadScript(obj);
2190
+ }
2191
+ async loadScript(nameOrObject) {
2192
+ // create states for scripts
2193
+ await this.createActiveObject(nameOrObject._id, !!nameOrObject?.common?.enabled);
2194
+ await this.createProblemObject(nameOrObject._id);
2195
+ return this.prepareScript(nameOrObject);
2196
+ }
2197
+ getAstroEvent(date, astroEvent, start, end, offsetMinutes, isDayEnd, latitude, longitude, useNextDay) {
2198
+ let ts = this.mods.suncalc.getTimes(date, latitude, longitude)[astroEvent];
2199
+ if (!ts || ts.getTime().toString() === 'NaN') {
2200
+ ts = isDayEnd ? getNextTimeEvent(end, useNextDay) : getNextTimeEvent(start, useNextDay);
2201
+ }
2202
+ ts.setMilliseconds(0);
2203
+ ts.setMinutes(ts.getMinutes() + (parseInt(offsetMinutes, 10) || 0));
2204
+ const [timeHoursStart, timeMinutesStart] = start.split(':');
2205
+ const nTimeHoursStart = parseInt(timeHoursStart, 10);
2206
+ const nTimeMinutesStart = parseInt(timeMinutesStart, 10) || 0;
2207
+ if (ts.getHours() < nTimeHoursStart ||
2208
+ (ts.getHours() === nTimeHoursStart && ts.getMinutes() < nTimeMinutesStart)) {
2209
+ ts = getNextTimeEvent(start, useNextDay);
2210
+ ts.setSeconds(0);
2211
+ }
2212
+ const [timeHoursEnd, timeMinutesEnd] = end.split(':');
2213
+ const nTimeHoursEnd = parseInt(timeHoursEnd, 10);
2214
+ const nTimeMinutesEnd = parseInt(timeMinutesEnd, 10) || 0;
2215
+ if (ts.getHours() > nTimeHoursEnd || (ts.getHours() === nTimeHoursEnd && ts.getMinutes() > nTimeMinutesEnd)) {
2216
+ ts = getNextTimeEvent(end, useNextDay);
2217
+ ts.setSeconds(0);
2218
+ }
2219
+ // if event in the past
2220
+ if (date > ts && useNextDay) {
2221
+ // take the next day
2222
+ ts.setDate(ts.getDate() + 1);
2223
+ }
2224
+ return ts;
2225
+ }
2226
+ async timeSchedule() {
2227
+ const now = new Date();
2228
+ let hours = now.getHours();
2229
+ const minutes = now.getMinutes();
2230
+ if (this.timeSettings.format12) {
2231
+ if (hours > 12) {
2232
+ hours -= 12;
2233
+ }
2234
+ }
2235
+ let sHours;
2236
+ if (this.timeSettings.leadingZeros) {
2237
+ sHours = hours.toString().padStart(2, '0');
2238
+ }
2239
+ else {
2240
+ sHours = hours.toString();
2241
+ }
2242
+ await this.setState('variables.dayTime', {
2243
+ val: `${sHours}:${minutes.toString().padStart(2, '0')}`,
2244
+ ack: true,
2245
+ });
2246
+ now.setMinutes(now.getMinutes() + 1);
2247
+ now.setSeconds(0);
2248
+ now.setMilliseconds(0);
2249
+ const interval = now.getTime() - Date.now();
2250
+ this.timeScheduleTimer = setTimeout(() => this.timeSchedule(), interval);
2251
+ }
2252
+ async dayTimeSchedules() {
2253
+ // get astrological event
2254
+ if (this.config.latitude === undefined ||
2255
+ this.config.longitude === undefined ||
2256
+ this.config.latitude === '' ||
2257
+ this.config.longitude === '' ||
2258
+ this.config.latitude === null ||
2259
+ this.config.longitude === null) {
2260
+ this.log.error('Longitude or latitude does not set. Cannot use astro.');
2261
+ return;
2262
+ }
2263
+ // Calculate the next event today
2264
+ const todayDate = getAstroStartOfDay();
2265
+ const nowDate = new Date();
2266
+ const todaySunrise = this.getAstroEvent(todayDate, this.config.sunriseEvent, this.config.sunriseLimitStart, this.config.sunriseLimitEnd, this.config.sunriseOffset, false, this.config.latitude, this.config.longitude);
2267
+ const todaySunset = this.getAstroEvent(todayDate, this.config.sunsetEvent, this.config.sunsetLimitStart, this.config.sunsetLimitEnd, this.config.sunsetOffset, true, this.config.latitude, this.config.longitude);
2268
+ // Sunrise
2269
+ let sunriseTimeout = todaySunrise.getTime() - nowDate.getTime();
2270
+ if (sunriseTimeout < 0 || sunriseTimeout > 3600000) {
2271
+ sunriseTimeout = 3600000;
2272
+ }
2273
+ // Sunset
2274
+ let sunsetTimeout = todaySunset.getTime() - nowDate.getTime();
2275
+ if (sunsetTimeout < 0 || sunsetTimeout > 3600000) {
2276
+ sunsetTimeout = 3600000;
2277
+ }
2278
+ const isDayTime = await this.getStateAsync('variables.isDayTime');
2279
+ let isDay;
2280
+ if (sunriseTimeout < 5000) {
2281
+ isDay = true;
2282
+ }
2283
+ else if (sunsetTimeout < 5000) {
2284
+ isDay = false;
2285
+ }
2286
+ else {
2287
+ // check if in between
2288
+ isDay = nowDate.getTime() > todaySunrise.getTime() - 60000 && nowDate <= todaySunset;
2289
+ }
2290
+ const valDayTime = isDayTime ? !!isDayTime.val : false;
2291
+ if (valDayTime !== isDay || isDayTime === null) {
2292
+ await this.setState('variables.isDayTime', isDay, true);
2293
+ }
2294
+ const dayLightSaving = await this.getStateAsync('variables.isDaylightSaving');
2295
+ const isDayLightSaving = dstOffsetAtDate(nowDate) !== 0;
2296
+ const val = dayLightSaving ? !!dayLightSaving.val : false;
2297
+ if (val !== isDayLightSaving || dayLightSaving === null) {
2298
+ await this.setState('variables.isDaylightSaving', isDayLightSaving, true);
2299
+ }
2300
+ let nextTimeout = sunriseTimeout;
2301
+ if (sunriseTimeout > sunsetTimeout) {
2302
+ nextTimeout = sunsetTimeout;
2303
+ }
2304
+ nextTimeout = nextTimeout - 3000;
2305
+ if (nextTimeout < 3000) {
2306
+ nextTimeout = 3000;
2307
+ }
2308
+ this.dayScheduleTimer = setTimeout(() => this.dayTimeSchedules(), nextTimeout);
2309
+ }
2310
+ stopTimeSchedules() {
2311
+ if (this.dayScheduleTimer) {
2312
+ clearTimeout(this.dayScheduleTimer);
2313
+ this.dayScheduleTimer = null;
2314
+ }
2315
+ if (this.sunScheduleTimer) {
2316
+ clearTimeout(this.sunScheduleTimer);
2317
+ this.sunScheduleTimer = null;
2318
+ }
2319
+ if (this.timeScheduleTimer) {
2320
+ clearTimeout(this.timeScheduleTimer);
2321
+ this.timeScheduleTimer = null;
2322
+ }
2323
+ }
2324
+ async patchFont() {
2325
+ let stat;
2326
+ let dbFile;
2327
+ let fileName = `${__dirname}/../admin/vs/base/browser/ui/codicons/codicon/codicon.ttf`;
2328
+ let jsonFileName = `${__dirname}/../admin/vsFont/codicon.json`;
2329
+ try {
2330
+ if ((0, node_fs_1.existsSync)(fileName)) {
2331
+ stat = (0, node_fs_1.statSync)(fileName);
2332
+ }
2333
+ if (!stat) {
2334
+ jsonFileName = `${__dirname}/admin/vsFont/codicon.json`;
2335
+ fileName = `${__dirname}/admin/vs/base/browser/ui/codicons/codicon/codicon.ttf`;
2336
+ if ((0, node_fs_1.existsSync)(fileName)) {
2337
+ stat = (0, node_fs_1.statSync)(fileName);
2338
+ }
2339
+ }
2340
+ const _dbFile = await this.readFileAsync('javascript.admin', `vs/base/browser/ui/codicons/codicon/codicon.ttf`);
2341
+ if (_dbFile?.file) {
2342
+ dbFile = _dbFile.file;
2343
+ }
2344
+ }
2345
+ catch {
2346
+ // ignore
2347
+ }
2348
+ if (stat?.size !== 73452 || dbFile?.byteLength !== 73452) {
2349
+ try {
2350
+ const buffer = Buffer.from(JSON.parse((0, node_fs_1.readFileSync)(jsonFileName).toString()), 'base64');
2351
+ const jszip = await Promise.resolve().then(() => __importStar(require('jszip')));
2352
+ const zip = await jszip.loadAsync(buffer);
2353
+ let data;
2354
+ if (zip) {
2355
+ data = await zip.file('codicon.ttf')?.async('arraybuffer');
2356
+ if (data?.byteLength !== 73452) {
2357
+ this.log.error(`Cannot patch font: invalid font file!`);
2358
+ return false;
2359
+ }
2360
+ }
2361
+ else {
2362
+ this.log.error(`Cannot patch font: invalid font file!`);
2363
+ return false;
2364
+ }
2365
+ (0, node_fs_1.writeFileSync)(fileName, Buffer.from(data));
2366
+ // upload this file
2367
+ await this.writeFileAsync('javascript.admin', 'vs/base/browser/ui/codicons/codicon/codicon.ttf', Buffer.from(data));
2368
+ return true;
2369
+ }
2370
+ catch (err) {
2371
+ this.log.error(`Cannot patch font: ${err}`);
2372
+ return false;
2373
+ }
2374
+ }
2375
+ return false;
2376
+ }
2377
+ async sunTimeSchedules() {
2378
+ if (this.config.createAstroStates) {
2379
+ if (!isNaN(this.config.longitude) && !isNaN(this.config.longitude)) {
2380
+ const calcDate = getAstroStartOfDay();
2381
+ const times = this.mods.suncalc.getTimes(calcDate, this.config.latitude, this.config.longitude);
2382
+ this.log.debug(`[sunTimeSchedules] Times: ${JSON.stringify(times)}`);
2383
+ for (const t in times) {
2384
+ try {
2385
+ const objId = `variables.astro.${t}`;
2386
+ await this.setObjectNotExistsAsync(objId, {
2387
+ type: 'state',
2388
+ common: {
2389
+ name: `Astro ${t}`,
2390
+ type: 'string',
2391
+ role: 'value',
2392
+ read: true,
2393
+ write: false,
2394
+ },
2395
+ native: {},
2396
+ });
2397
+ if (times[t] !== null && !isNaN(times[t].getTime())) {
2398
+ const timeFormatted = formatHoursMinutesSeconds(times[t]);
2399
+ await this.setState(objId, {
2400
+ val: timeFormatted,
2401
+ c: times[t].toISOString(),
2402
+ ack: true,
2403
+ });
2404
+ }
2405
+ else {
2406
+ await this.setState(objId, { val: null, c: 'n/a', ack: true, q: 0x01 });
2407
+ }
2408
+ }
2409
+ catch (err) {
2410
+ this.log.error(`[sunTimeSchedules] Unable to set state for astro time "${t}" (${times[t].getTime()}): ${err}`);
2411
+ }
2412
+ }
2413
+ const todayDate = new Date();
2414
+ todayDate.setHours(0);
2415
+ todayDate.setMinutes(0);
2416
+ todayDate.setSeconds(1);
2417
+ todayDate.setMilliseconds(0);
2418
+ todayDate.setDate(todayDate.getDate() + 1);
2419
+ this.log.debug(`[sunTimeSchedules] Next: ${todayDate.toISOString()}`);
2420
+ this.sunScheduleTimer = setTimeout(() => this.sunTimeSchedules(), todayDate.getTime() - Date.now());
2421
+ }
2422
+ }
2423
+ else {
2424
+ // remove astro states if disabled
2425
+ this.delObject('variables.astro', { recursive: true });
2426
+ }
2427
+ }
2428
+ /**
2429
+ * Redirects the virtual-tsc log output to the ioBroker log
2430
+ */
2431
+ tsLog = (message, severity) => {
2432
+ // shift the severities around, we don't care about the small details
2433
+ if (!severity || severity === 'info') {
2434
+ severity = 'debug';
2435
+ }
2436
+ else if (severity === 'debug') {
2437
+ // Don't spam build logs on Travis
2438
+ if (isCI) {
2439
+ return;
2440
+ }
2441
+ severity = 'silly';
2442
+ }
2443
+ if (this?.log) {
2444
+ this.log[severity](message);
2445
+ }
2446
+ else {
2447
+ console.log(`[${severity.toUpperCase()}] ${message}`);
2448
+ }
2449
+ };
2450
+ addGetProperty(object) {
2451
+ try {
2452
+ Object.defineProperty(object, 'get', {
2453
+ value: function (id) {
2454
+ return this[id] || this[`${this.namespace}.${id}`];
2455
+ },
2456
+ enumerable: false,
2457
+ });
2458
+ }
2459
+ catch {
2460
+ console.error('Cannot install get property');
2461
+ }
2462
+ }
2463
+ /**
2464
+ * Add declarations for global scripts
2465
+ *
2466
+ * @param scriptID - The current script the declarations were generated from
2467
+ * @param declarations - Declarations from script
2468
+ */
2469
+ provideDeclarationsForGlobalScript(scriptID, declarations) {
2470
+ // Remember which declarations this global script had access to,
2471
+ // we need this so the editor doesn't show a duplicate identifier error
2472
+ if (this.globalDeclarations != null && this.globalDeclarations !== '') {
2473
+ this.knownGlobalDeclarationsByScript[scriptID] = this.globalDeclarations;
2474
+ }
2475
+ // and concatenate the global declarations for the next scripts
2476
+ this.globalDeclarations += `${declarations}\n`;
2477
+ // remember all previously generated global declarations,
2478
+ // so global scripts can reference each other
2479
+ const globalDeclarationPath = 'global.d.ts';
2480
+ tsAmbient[globalDeclarationPath] = this.globalDeclarations;
2481
+ // make sure the next script compilation has access to the updated declarations
2482
+ this.tsServer.provideAmbientDeclarations({
2483
+ [globalDeclarationPath]: this.globalDeclarations,
2484
+ });
2485
+ jsDeclarationServer.provideAmbientDeclarations({
2486
+ [globalDeclarationPath]: this.globalDeclarations,
2487
+ });
2488
+ }
2489
+ fixLineNo(line) {
2490
+ if (line.includes('javascript.js:')) {
2491
+ return line;
2492
+ }
2493
+ if (!/scripts?\.js[.\\/]/.test(line)) {
2494
+ return line;
2495
+ }
2496
+ if (/:(\d+):/.test(line)) {
2497
+ line = line.replace(/:(\d+):/, (_$0, $1) => `:${$1 > this.globalScriptLines + 1 ? $1 - this.globalScriptLines - 1 : $1}:`); // one line for 'async function ()'
2498
+ }
2499
+ else {
2500
+ line = line.replace(/:(\d+)$/, (_$0, $1) => `:${$1 > this.globalScriptLines + 1 ? $1 - this.globalScriptLines - 1 : $1}`); // one line for 'async function ()'
2501
+ }
2502
+ return line;
2503
+ }
2504
+ async debugStop() {
2505
+ if (this.debugState.child) {
2506
+ this.debugSendToInspector({ cmd: 'end' });
2507
+ this.debugState.endTimeout = setTimeout(() => {
2508
+ this.debugState.endTimeout = null;
2509
+ this.debugState.child?.kill('SIGTERM');
2510
+ }, 500);
2511
+ this.debugState.promiseOnEnd = this.debugState.promiseOnEnd || Promise.resolve(0);
2512
+ }
2513
+ else {
2514
+ this.debugState.promiseOnEnd = Promise.resolve(0);
2515
+ }
2516
+ await this.debugState.promiseOnEnd;
2517
+ this.debugState.child = null;
2518
+ this.debugState.running = false;
2519
+ this.debugState.scriptName = '';
2520
+ if (this.debugState.endTimeout) {
2521
+ clearTimeout(this.debugState.endTimeout);
2522
+ this.debugState.endTimeout = null;
2523
+ }
2524
+ }
2525
+ async debugDisableScript(id) {
2526
+ if (id) {
2527
+ const obj = this.objects[id];
2528
+ if (obj?.common?.enabled) {
2529
+ await this.extendForeignObjectAsync(obj._id, { common: { enabled: false } });
2530
+ }
2531
+ }
2532
+ }
2533
+ debugSendToInspector(message) {
2534
+ if (this.debugState.child) {
2535
+ try {
2536
+ this.log.info(`send to debugger: ${message}`);
2537
+ this.debugState.child.send(message);
2538
+ }
2539
+ catch {
2540
+ void this.debugStop().then(() => this.log.info(`${this.debugState.scriptName}: Debugging was stopped, because started in normal mode`));
2541
+ }
2542
+ }
2543
+ else {
2544
+ this.log.error(`${this.debugState.scriptName}: Cannot send command to terminated inspector`);
2545
+ void this.setState('debug.from', JSON.stringify({ cmd: 'error', error: `Cannot send command to terminated inspector`, id: 1 }), true);
2546
+ }
2547
+ }
2548
+ debugStart(data) {
2549
+ if (Date.now() - this.debugState.started < 1000) {
2550
+ console.log('Start ignored');
2551
+ return;
2552
+ }
2553
+ this.debugState.started = Date.now();
2554
+ // stop the script if it's running
2555
+ void this.debugDisableScript(data.scriptName)
2556
+ .then(() => this.debugStop())
2557
+ .then(() => {
2558
+ if (data.adapter) {
2559
+ this.debugState.adapterInstance = data.adapter;
2560
+ this.debugState.scriptName = '';
2561
+ }
2562
+ else {
2563
+ this.debugState.adapterInstance = '';
2564
+ this.debugState.scriptName = data.scriptName;
2565
+ }
2566
+ this.debugState.breakOnStart = data.breakOnStart;
2567
+ this.debugState.promiseOnEnd = new Promise(resolve => {
2568
+ const options = {
2569
+ stdio: ['ignore', 'inherit', 'inherit', 'ipc'],
2570
+ //stdio: ['pipe', 'pipe', 'pipe', 'ipc']
2571
+ };
2572
+ const args = [];
2573
+ if (this.debugState.adapterInstance) {
2574
+ args.push('--breakOnStart');
2575
+ }
2576
+ this.debugState.child = (0, node_child_process_1.fork)(`${__dirname}/lib/inspect.js`, args, options);
2577
+ /*debugState.child.stdout.setEncoding('utf8');
2578
+ debugState.child.stderr.setEncoding('utf8');
2579
+ debugState.child.stdout.on('data', childPrint);
2580
+ debugState.child.stderr.on('data', childPrint);*/
2581
+ this.debugState.child?.on('message', (message) => {
2582
+ let oMessage;
2583
+ if (typeof message === 'string') {
2584
+ try {
2585
+ oMessage = JSON.parse(message);
2586
+ }
2587
+ catch {
2588
+ return this.log.error(`Cannot parse message from inspector: ${message}`);
2589
+ }
2590
+ }
2591
+ else {
2592
+ oMessage = message;
2593
+ }
2594
+ if (oMessage.cmd !== 'ready') {
2595
+ void this.setState('debug.from', JSON.stringify(oMessage), true);
2596
+ }
2597
+ switch (oMessage.cmd) {
2598
+ case 'ready': {
2599
+ this.debugSendToInspector({
2600
+ cmd: 'start',
2601
+ scriptName: this.debugState.scriptName,
2602
+ adapterInstance: this.debugState.adapterInstance,
2603
+ instance: this.instance,
2604
+ });
2605
+ break;
2606
+ }
2607
+ case 'watched': {
2608
+ //console.log(`WATCHED: ${JSON.stringify(oMessage)}`);
2609
+ break;
2610
+ }
2611
+ case 'paused': {
2612
+ this.debugState.paused = true;
2613
+ console.log(`host: PAUSED`);
2614
+ break;
2615
+ }
2616
+ case 'resumed': {
2617
+ this.debugState.paused = false;
2618
+ //console.log(`STARTED`);
2619
+ break;
2620
+ }
2621
+ case 'log': {
2622
+ console.log(`[${oMessage.severity}] ${oMessage.text}`);
2623
+ break;
2624
+ }
2625
+ case 'readyToDebug': {
2626
+ console.log(`host: readyToDebug (set breakpoints): [${oMessage.scriptId}] ${oMessage.script}`);
2627
+ break;
2628
+ }
2629
+ }
2630
+ });
2631
+ this.debugState.child?.on('error', error => {
2632
+ this.log.error(`Cannot start inspector: ${error}`);
2633
+ void this.setState('debug.from', JSON.stringify({ cmd: 'error', error }), true);
2634
+ });
2635
+ this.debugState.child?.on('exit', async (code) => {
2636
+ if (code) {
2637
+ await this.setState('debug.from', JSON.stringify({ cmd: 'error', error: `invalid response code: ${code}` }), true);
2638
+ }
2639
+ await this.setState('debug.from', JSON.stringify({ cmd: 'debugStopped', code }), true);
2640
+ this.debugState.child = null;
2641
+ resolve(code);
2642
+ });
2643
+ });
2644
+ });
2645
+ }
2646
+ }
2647
+ function patternMatching(event, patternFunctions) {
2648
+ let matched = false;
2649
+ for (let i = 0, len = patternFunctions.length; i < len; i++) {
2650
+ if (patternFunctions[i](event)) {
2651
+ if (patternFunctions.logic === 'or') {
2652
+ return true;
2653
+ }
2654
+ matched = true;
2655
+ }
2656
+ else if (patternFunctions.logic === 'and') {
2657
+ return false;
2658
+ }
2659
+ }
2660
+ return matched;
2661
+ }
2662
+ // If started as allInOne mode => return function to create instance
2663
+ if (require.main !== module) {
2664
+ // Export the constructor in compact mode
2665
+ module.exports = (options) => new JavaScript(options);
2666
+ }
2667
+ else {
2668
+ // otherwise start the instance directly
2669
+ (() => new JavaScript())();
2670
+ }
2671
+ //# sourceMappingURL=main.js.map