iobroker.nut2 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/main.js ADDED
@@ -0,0 +1,521 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var main_exports = {};
30
+ __export(main_exports, {
31
+ NutAdapter: () => NutAdapter
32
+ });
33
+ module.exports = __toCommonJS(main_exports);
34
+ var utils = __toESM(require("@iobroker/adapter-core"));
35
+ var import_adapter_core = require("@iobroker/adapter-core");
36
+ var import_node_path = require("node:path");
37
+ var import_coerce = require("./lib/coerce");
38
+ var import_message_router = require("./lib/message-router");
39
+ var import_nut_client = require("./lib/nut-client");
40
+ var import_state_manager = require("./lib/state-manager");
41
+ var import_type_detector = require("./lib/type-detector");
42
+ class NutAdapter extends utils.Adapter {
43
+ client = null;
44
+ stateManager = null;
45
+ pollTimer = void 0;
46
+ isPolling = false;
47
+ lastErrorCode = "";
48
+ failedUps = /* @__PURE__ */ new Set();
49
+ discoveredUps = /* @__PURE__ */ new Map();
50
+ authenticated = false;
51
+ enrichedUps = /* @__PURE__ */ new Set();
52
+ testClients = /* @__PURE__ */ new Set();
53
+ subscribed = false;
54
+ unloaded = false;
55
+ everConnected = false;
56
+ /** @param options Adapter options forwarded to the ioBroker base class. */
57
+ constructor(options = {}) {
58
+ super({ ...options, name: "nut2" });
59
+ this.on("ready", this.onReady.bind(this));
60
+ this.on("stateChange", this.onStateChange.bind(this));
61
+ this.on("unload", this.onUnload.bind(this));
62
+ this.on("message", this.onMessage.bind(this));
63
+ }
64
+ /** The native config, typed — single cast point for all config reads. */
65
+ nutConfig() {
66
+ return this.config;
67
+ }
68
+ // Factory seams — production builds the real collaborators; the orchestration
69
+ // unit tests (src/main.test.ts) override these fields with fakes so onReady,
70
+ // onConnected and poll can run without sockets or a js-controller.
71
+ makeClient = (...args) => new import_nut_client.NutClient(...args);
72
+ makeStateManager = () => new import_state_manager.StateManager(this);
73
+ // Single source for the {debug,warn,info} logger passed to the NUT client, the test-client
74
+ // factory and the message router — avoids rebuilding the same wrapper at three call sites.
75
+ get nutLogger() {
76
+ return {
77
+ debug: (m) => this.log.debug(m),
78
+ warn: (m) => this.log.warn(m),
79
+ info: (m) => this.log.info(m)
80
+ };
81
+ }
82
+ async onReady() {
83
+ try {
84
+ await import_adapter_core.I18n.init((0, import_node_path.join)(this.adapterDir, "admin"), this);
85
+ const config = this.nutConfig();
86
+ this.log.debug(
87
+ `onReady: starting (host='${config.host}', port=${JSON.stringify(config.port)}, pollInterval=${JSON.stringify(config.pollInterval)}s)`
88
+ );
89
+ await this.setStateChangedAsync("info.connection", { val: false, ack: true });
90
+ const host = (0, import_coerce.coerceHost)(config.host);
91
+ if (!host) {
92
+ this.log.error("NUT server host is required \u2014 check adapter configuration");
93
+ return;
94
+ }
95
+ const port = (0, import_coerce.coercePort)(config.port);
96
+ const commandTimeoutMs = (0, import_coerce.coerceCommandTimeoutMs)(config.commandTimeout);
97
+ this.log.debug(`commandTimeout: raw=${JSON.stringify(config.commandTimeout)} resolved=${commandTimeoutMs}ms`);
98
+ const localAddress = (0, import_coerce.localAddressOf)(config.networkInterface);
99
+ this.client = this.makeClient(host, port, {
100
+ localAddress,
101
+ commandTimeout: commandTimeoutMs,
102
+ useTls: !!config.useTls,
103
+ tlsRejectUnauthorized: !!config.tlsRejectUnauthorized,
104
+ // Inject the adapter-managed timers so the client's command/reconnect timeouts are
105
+ // tracked and auto-cleared on unload (no native setTimeout leaks).
106
+ setTimer: (cb, ms) => this.setTimeout(cb, ms),
107
+ clearTimer: (h) => {
108
+ if (h != null) {
109
+ this.clearTimeout(h);
110
+ }
111
+ },
112
+ logger: this.nutLogger
113
+ });
114
+ this.stateManager = this.makeStateManager();
115
+ this.client.setOnConnect(() => {
116
+ void this.onConnected().catch((err) => this.log.error(`onConnected failed: ${(0, import_coerce.errText)(err)}`));
117
+ });
118
+ this.client.setOnFatal((err) => this.onConnectFatal(err));
119
+ this.client.start();
120
+ } catch (err) {
121
+ this.log.error(`onReady failed: ${(0, import_coerce.errText)(err)}`);
122
+ }
123
+ }
124
+ /**
125
+ * Idempotent post-connect setup, run on the initial connect AND every reconnect (the client's
126
+ * single retry loop drives both): discover UPSes, (re-)authenticate, refresh command buttons,
127
+ * poll, and arm the poll timer + subscription once. Auth failure goes yellow and stops the
128
+ * loop via destroy(). Making "initial == reconnect" one path keeps the two from drifting.
129
+ */
130
+ async onConnected() {
131
+ var _a;
132
+ if (this.unloaded || !this.client || !this.stateManager) {
133
+ return;
134
+ }
135
+ const config = this.nutConfig();
136
+ const host = (_a = (0, import_coerce.coerceHost)(config.host)) != null ? _a : "";
137
+ const port = (0, import_coerce.coercePort)(config.port);
138
+ const pollSec = (0, import_coerce.coercePollIntervalSec)(config.pollInterval);
139
+ try {
140
+ this.enrichedUps.clear();
141
+ await this.discover();
142
+ if (!await this.authenticateIfConfigured(host, port)) {
143
+ return;
144
+ }
145
+ await this.setupCommandButtons();
146
+ await this.poll();
147
+ this.armPollTimer(config.pollInterval, pollSec);
148
+ if (!this.subscribed && (config.enableCommands || config.enableSetVar)) {
149
+ await this.subscribeStatesAsync("*");
150
+ this.subscribed = true;
151
+ }
152
+ if (this.everConnected) {
153
+ this.log.info(`Reconnected to NUT server ${host}:${port} \u2014 ${this.discoveredUps.size} UPS(es)`);
154
+ } else {
155
+ this.everConnected = true;
156
+ const authStatus = this.authenticated ? "authenticated" : "no credentials";
157
+ this.log.info(
158
+ `NUT adapter started \u2014 ${this.discoveredUps.size} UPS(es) on ${host}:${port}, polling every ${pollSec}s (${authStatus})`
159
+ );
160
+ }
161
+ } catch (err) {
162
+ this.log.error(`Post-connect setup failed: ${(0, import_coerce.errText)(err)}`);
163
+ this.armPollTimer(config.pollInterval, pollSec);
164
+ }
165
+ }
166
+ /**
167
+ * Authenticate when credentials are configured. USERNAME/PASSWORD is all that GET/LIST + SET VAR
168
+ * + INSTCMD need; we deliberately do NOT send LOGIN per UPS (NUT permits only one LOGIN per
169
+ * connection, so a multi-UPS server fails the second with ALREADY-LOGGED-IN, and LOGIN only
170
+ * matters for upsmon shutdown coordination, which this adapter does not do).
171
+ *
172
+ * @param host NUT server host (for logging)
173
+ * @param port NUT server port (for logging)
174
+ * @returns true to continue setup; false when authentication failed and the client was destroyed
175
+ */
176
+ async authenticateIfConfigured(host, port) {
177
+ this.authenticated = false;
178
+ const config = this.nutConfig();
179
+ if (!config.username || !config.password || !this.client) {
180
+ return true;
181
+ }
182
+ try {
183
+ await this.client.authenticate(config.username, config.password);
184
+ this.authenticated = true;
185
+ this.log.debug(`Authenticated to NUT server ${host}:${port}`);
186
+ return true;
187
+ } catch (err) {
188
+ this.log.error(`Authentication failed: ${(0, import_coerce.errText)(err)} \u2014 check NUT server credentials`);
189
+ this.log.info(
190
+ `Authentication required \u2014 adapter is idle (yellow) until the credentials are corrected; use the connection test in admin to verify them`
191
+ );
192
+ this.client.destroy();
193
+ await this.setStateChangedAsync("info.connection", { val: false, ack: true });
194
+ return false;
195
+ }
196
+ }
197
+ /**
198
+ * Create instant-command button states for every discovered UPS. Only runs when we authenticated
199
+ * and commands are enabled; each UPS is best-effort.
200
+ */
201
+ async setupCommandButtons() {
202
+ if (!this.authenticated || !this.nutConfig().enableCommands || !this.client || !this.stateManager) {
203
+ return;
204
+ }
205
+ for (const ups of this.discoveredUps.keys()) {
206
+ try {
207
+ const commands = await this.client.listCmd(ups);
208
+ await this.stateManager.createCommandButtons(ups, commands);
209
+ this.log.debug(`Created ${commands.length} command buttons for ${ups}`);
210
+ } catch (err) {
211
+ this.log.debug(`Failed to list commands for ${ups}: ${(0, import_coerce.errText)(err)}`);
212
+ }
213
+ }
214
+ }
215
+ /**
216
+ * Arm the periodic poll timer once. Called on the normal setup path and again from the
217
+ * post-connect error handler, so a non-connection failure on a live socket still recovers.
218
+ *
219
+ * @param rawInterval Raw configured poll interval, logged for diagnostics
220
+ * @param pollSec Resolved poll interval in seconds
221
+ */
222
+ armPollTimer(rawInterval, pollSec) {
223
+ if (this.unloaded || this.pollTimer !== void 0) {
224
+ return;
225
+ }
226
+ this.log.debug(`pollInterval: raw=${JSON.stringify(rawInterval)} resolved=${pollSec}s`);
227
+ this.pollTimer = this.setInterval(() => {
228
+ void this.poll();
229
+ }, pollSec * 1e3);
230
+ }
231
+ /**
232
+ * The persistent connection failed fatally (TLS misconfiguration). The client already stopped
233
+ * retrying; stay alive + yellow so the admin connection-test button remains usable.
234
+ *
235
+ * @param err The fatal connect/STARTTLS error
236
+ */
237
+ onConnectFatal(err) {
238
+ var _a, _b;
239
+ const config = this.nutConfig();
240
+ const host = (_a = (0, import_coerce.coerceHost)(config.host)) != null ? _a : "";
241
+ const port = (0, import_coerce.coercePort)(config.port);
242
+ this.log.error(
243
+ `TLS connection to NUT server ${host}:${port} failed: ${(0, import_coerce.errText)(err)} \u2014 verify the server offers STARTTLS and check the certificate setting`
244
+ );
245
+ (_b = this.client) == null ? void 0 : _b.destroy();
246
+ void this.setStateChangedAsync("info.connection", { val: false, ack: true }).catch(() => {
247
+ });
248
+ }
249
+ async discover() {
250
+ if (!this.client || !this.stateManager) {
251
+ return;
252
+ }
253
+ const upsList = await this.client.listUps();
254
+ this.log.debug(`Discovered ${upsList.length} UPS(es): ${upsList.map((u) => u.name).join(", ")}`);
255
+ this.discoveredUps.clear();
256
+ for (const ups of upsList) {
257
+ this.discoveredUps.set(ups.name, ups);
258
+ await this.stateManager.ensureUpsDevice(ups.name, ups.description);
259
+ }
260
+ const knownNames = new Set(this.discoveredUps.keys());
261
+ await this.stateManager.cleanupRemovedUps(knownNames);
262
+ await this.stateManager.cleanupLegacyObjects(knownNames);
263
+ for (const name of this.failedUps) {
264
+ if (!knownNames.has(name)) {
265
+ this.failedUps.delete(name);
266
+ }
267
+ }
268
+ for (const name of this.enrichedUps) {
269
+ if (!knownNames.has(name)) {
270
+ this.enrichedUps.delete(name);
271
+ }
272
+ }
273
+ }
274
+ classifyError(err) {
275
+ if (err instanceof import_nut_client.NutError) {
276
+ return err.code;
277
+ }
278
+ if (!(err instanceof Error)) {
279
+ return "UNKNOWN";
280
+ }
281
+ const code = err.code;
282
+ if (code === "ENOTFOUND" || code === "ECONNREFUSED" || code === "ECONNRESET" || code === "ENETUNREACH" || code === "EHOSTUNREACH" || code === "EAI_AGAIN") {
283
+ return "NETWORK";
284
+ }
285
+ if (code === "ETIMEDOUT" || err.message.includes("timed out")) {
286
+ return "TIMEOUT";
287
+ }
288
+ return code || "UNKNOWN";
289
+ }
290
+ async poll() {
291
+ var _a, _b, _c, _d;
292
+ if (this.isPolling) {
293
+ this.log.debug("Skipping poll \u2014 previous poll still running");
294
+ return;
295
+ }
296
+ if (!this.client || !this.stateManager) {
297
+ return;
298
+ }
299
+ this.log.debug(`poll: starting (lastErrorCode='${this.lastErrorCode}', upsCount=${this.discoveredUps.size})`);
300
+ this.isPolling = true;
301
+ try {
302
+ for (const upsName of this.discoveredUps.keys()) {
303
+ try {
304
+ const [variables, rwVars] = await Promise.all([
305
+ this.client.listVar(upsName),
306
+ this.nutConfig().enableSetVar ? this.client.listRw(upsName).catch((err) => {
307
+ this.log.debug(`LIST RW ${upsName} failed (non-critical): ${(0, import_coerce.errText)(err)}`);
308
+ return [];
309
+ }) : Promise.resolve([])
310
+ ]);
311
+ const rwNames = new Set(rwVars.map((v) => v.name));
312
+ await this.stateManager.updateVariables(upsName, variables, rwNames);
313
+ const upsDesc = this.discoveredUps.get(upsName);
314
+ await this.stateManager.updateDeviceName(upsName, (_a = upsDesc == null ? void 0 : upsDesc.description) != null ? _a : "", variables);
315
+ const statusVar = variables.find((v) => v.name === "ups.status");
316
+ if (statusVar) {
317
+ const chargerStatus = (_b = variables.find((v) => v.name === "battery.charger.status")) == null ? void 0 : _b.value;
318
+ await this.stateManager.updateStatusFlags(upsName, statusVar.value, chargerStatus);
319
+ }
320
+ await this.enrichWritableVars(upsName, rwVars);
321
+ await this.setStateChangedAsync(`${upsName}.info.reachable`, { val: true, ack: true });
322
+ if (this.failedUps.has(upsName)) {
323
+ this.log.info(`UPS '${upsName}' recovered`);
324
+ this.failedUps.delete(upsName);
325
+ }
326
+ } catch (err) {
327
+ await this.setStateChangedAsync(`${upsName}.info.reachable`, { val: false, ack: true });
328
+ const msg = `Failed to poll UPS '${upsName}': ${(0, import_coerce.errText)(err)}`;
329
+ if (this.failedUps.has(upsName)) {
330
+ this.log.debug(msg);
331
+ } else {
332
+ const isDataStale = err instanceof import_nut_client.NutError && err.code === "DATA-STALE";
333
+ if (isDataStale) {
334
+ this.log.warn(`UPS '${upsName}': driver reports stale data \u2014 keeping existing states`);
335
+ } else {
336
+ this.log.warn(msg);
337
+ }
338
+ this.failedUps.add(upsName);
339
+ }
340
+ }
341
+ }
342
+ await this.setStateChangedAsync("info.connection", { val: (_d = (_c = this.client) == null ? void 0 : _c.isConnected) != null ? _d : false, ack: true });
343
+ if (this.lastErrorCode) {
344
+ this.log.info("Connection restored");
345
+ this.lastErrorCode = "";
346
+ }
347
+ } catch (err) {
348
+ const errMsg = (0, import_coerce.errText)(err);
349
+ const errorCode = this.classifyError(err);
350
+ const isRepeat = errorCode === this.lastErrorCode;
351
+ this.lastErrorCode = errorCode;
352
+ if (isRepeat) {
353
+ this.log.debug(`Poll failed (ongoing): ${errMsg}`);
354
+ } else if (errorCode === "NETWORK") {
355
+ this.log.warn("Cannot reach NUT server \u2014 will keep retrying");
356
+ } else {
357
+ this.log.error(`Poll failed: ${errMsg}`);
358
+ }
359
+ await this.setStateChangedAsync("info.connection", { val: false, ack: true });
360
+ } finally {
361
+ this.isPolling = false;
362
+ }
363
+ }
364
+ /**
365
+ * Enrich writable variables with ENUM (common.states) and RANGE (min/max) metadata, once per UPS
366
+ * per connection (guarded by enrichedUps). Each query is best-effort — a driver that does not
367
+ * support LIST ENUM/RANGE just logs at debug.
368
+ *
369
+ * @param upsName UPS identifier
370
+ * @param rwVars Writable variables from LIST RW
371
+ */
372
+ async enrichWritableVars(upsName, rwVars) {
373
+ if (!this.client || !this.stateManager || this.enrichedUps.has(upsName) || rwVars.length === 0) {
374
+ return;
375
+ }
376
+ for (const rw of rwVars) {
377
+ const stateId = (0, import_state_manager.nutVarToStateId)(upsName, rw.name);
378
+ const isBoolean = (0, import_type_detector.detectType)(rw.name, rw.value, true).type === "boolean";
379
+ if (!isBoolean) {
380
+ try {
381
+ const enumVals = await this.client.listEnum(upsName, rw.name);
382
+ if (enumVals.length > 0) {
383
+ const states = {};
384
+ for (const v of enumVals) {
385
+ states[v] = v;
386
+ }
387
+ await this.stateManager.enrichStateMetadata(stateId, { states });
388
+ }
389
+ } catch (err) {
390
+ this.log.debug(`LIST ENUM ${upsName} ${rw.name}: not supported (${(0, import_coerce.errText)(err)})`);
391
+ }
392
+ }
393
+ try {
394
+ const ranges = await this.client.listRange(upsName, rw.name);
395
+ if (ranges.length > 0) {
396
+ const min = (0, import_coerce.parseDecimal)(ranges[0].min);
397
+ const max = (0, import_coerce.parseDecimal)(ranges[0].max);
398
+ const patch = {};
399
+ if (!Number.isNaN(min)) {
400
+ patch.min = min;
401
+ }
402
+ if (!Number.isNaN(max)) {
403
+ patch.max = max;
404
+ }
405
+ await this.stateManager.enrichStateMetadata(stateId, patch);
406
+ }
407
+ } catch (err) {
408
+ this.log.debug(`LIST RANGE ${upsName} ${rw.name}: not supported (${(0, import_coerce.errText)(err)})`);
409
+ }
410
+ }
411
+ this.enrichedUps.add(upsName);
412
+ }
413
+ async onStateChange(id, state) {
414
+ var _a, _b, _c, _d;
415
+ try {
416
+ if (!state || state.ack) {
417
+ return;
418
+ }
419
+ const config = this.nutConfig();
420
+ const localId = id.replace(`${this.namespace}.`, "");
421
+ this.log.debug(`onStateChange: ${localId} val=${JSON.stringify(state.val)}`);
422
+ if (!this.client) {
423
+ this.log.debug(`onStateChange: ignoring ${localId} \u2014 no client connection`);
424
+ return;
425
+ }
426
+ const parts = localId.split(".");
427
+ if (parts.length < 3) {
428
+ this.log.debug(`onStateChange: unexpected id structure '${localId}', ignoring`);
429
+ return;
430
+ }
431
+ const upsName = parts[0];
432
+ if (!this.discoveredUps.has(upsName)) {
433
+ this.log.debug(`onStateChange: unknown UPS '${upsName}', ignoring`);
434
+ return;
435
+ }
436
+ if (parts[1] === "commands") {
437
+ if (!config.enableCommands) {
438
+ this.log.warn(`Command blocked \u2014 enableCommands is disabled: ${localId}`);
439
+ return;
440
+ }
441
+ const cmdName = (_b = (_a = this.stateManager) == null ? void 0 : _a.nutNameForState(localId)) != null ? _b : parts.slice(2).join(".").replace(/-/g, ".");
442
+ this.log.debug(`INSTCMD ${upsName} ${cmdName}`);
443
+ try {
444
+ await this.client.instCmd(upsName, cmdName);
445
+ this.log.info(`Command executed: ${cmdName} on ${upsName}`);
446
+ } catch (err) {
447
+ this.log.error(`Command failed: ${cmdName} on ${upsName} \u2014 ${(0, import_coerce.errText)(err)}`);
448
+ }
449
+ await this.setState(id, { val: false, ack: true });
450
+ return;
451
+ }
452
+ if (!config.enableSetVar) {
453
+ this.log.warn(`SET VAR blocked \u2014 enableSetVar is disabled: ${localId}`);
454
+ return;
455
+ }
456
+ const varName = (_d = (_c = this.stateManager) == null ? void 0 : _c.nutNameForState(localId)) != null ? _d : `${parts[1]}.${parts.slice(2).join(".").replace(/-/g, ".")}`;
457
+ const value = typeof state.val === "boolean" ? state.val ? "yes" : "no" : String(state.val);
458
+ this.log.debug(`SET VAR ${upsName} ${varName} "${value}"`);
459
+ try {
460
+ await this.client.setVar(upsName, varName, value);
461
+ await this.setState(id, { val: state.val, ack: true });
462
+ this.log.info(`Variable set: ${varName} = "${value}" on ${upsName}`);
463
+ } catch (err) {
464
+ this.log.error(`SET VAR failed: ${varName} on ${upsName} \u2014 ${(0, import_coerce.errText)(err)}`);
465
+ }
466
+ } catch (err) {
467
+ this.log.error(`onStateChange failed: ${(0, import_coerce.errText)(err)}`);
468
+ }
469
+ }
470
+ async onMessage(obj) {
471
+ try {
472
+ await (0, import_message_router.dispatchMessage)(obj, {
473
+ log: this.nutLogger,
474
+ sendTo: this.sendTo.bind(this),
475
+ createTestClient: (0, import_message_router.makeTestClientFactory)(import_nut_client.NutClient, this.nutLogger),
476
+ onTestClientCreated: (client) => {
477
+ this.testClients.add(client);
478
+ },
479
+ onTestClientDone: (client) => {
480
+ this.testClients.delete(client);
481
+ }
482
+ });
483
+ } catch (err) {
484
+ this.log.error(`onMessage failed: ${(0, import_coerce.errText)(err)}`);
485
+ }
486
+ }
487
+ onUnload(callback) {
488
+ var _a, _b;
489
+ try {
490
+ this.unloaded = true;
491
+ if (this.pollTimer) {
492
+ this.clearInterval(this.pollTimer);
493
+ this.pollTimer = void 0;
494
+ }
495
+ if (this.authenticated) {
496
+ (_a = this.client) == null ? void 0 : _a.shutdown();
497
+ } else {
498
+ (_b = this.client) == null ? void 0 : _b.destroy();
499
+ }
500
+ for (const tc of this.testClients) {
501
+ tc.destroy();
502
+ }
503
+ this.testClients.clear();
504
+ void this.setState("info.connection", { val: false, ack: true }).catch(() => {
505
+ });
506
+ } catch (err) {
507
+ this.log.debug(`onUnload error (ignored): ${(0, import_coerce.errText)(err)}`);
508
+ }
509
+ callback();
510
+ }
511
+ }
512
+ if (require.main !== module) {
513
+ module.exports = (options) => new NutAdapter(options);
514
+ } else {
515
+ (() => new NutAdapter())();
516
+ }
517
+ // Annotate the CommonJS export names for ESM import in node:
518
+ 0 && (module.exports = {
519
+ NutAdapter
520
+ });
521
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/main.ts"],
4
+ "sourcesContent": ["import * as utils from \"@iobroker/adapter-core\";\nimport { I18n } from \"@iobroker/adapter-core\";\nimport { join } from \"node:path\";\nimport {\n coerceCommandTimeoutMs,\n coerceHost,\n coercePollIntervalSec,\n coercePort,\n errText,\n localAddressOf,\n parseDecimal,\n} from \"./lib/coerce\";\nimport { dispatchMessage, makeTestClientFactory } from \"./lib/message-router\";\nimport { NutClient, NutError } from \"./lib/nut-client\";\nimport { nutVarToStateId, StateManager } from \"./lib/state-manager\";\nimport { detectType } from \"./lib/type-detector\";\nimport type { AdapterConfig, NutLogger, NutVariable, UpsInfo } from \"./lib/types\";\n\n/**\n * NUT adapter \u2014 lifecycle, polling, command/SET-VAR dispatch.\n * Exported so the orchestration unit tests can drive its handlers directly.\n */\nexport class NutAdapter extends utils.Adapter {\n private client: NutClient | null = null;\n private stateManager: StateManager | null = null;\n private pollTimer: ioBroker.Interval | undefined = undefined;\n private isPolling = false;\n private lastErrorCode = \"\";\n private failedUps = new Set<string>();\n private discoveredUps = new Map<string, UpsInfo>();\n private authenticated = false;\n private enrichedUps = new Set<string>();\n private testClients = new Set<NutClient>();\n private subscribed = false;\n private unloaded = false;\n private everConnected = false;\n\n /** @param options Adapter options forwarded to the ioBroker base class. */\n constructor(options: Partial<utils.AdapterOptions> = {}) {\n super({ ...options, name: \"nut2\" });\n this.on(\"ready\", this.onReady.bind(this));\n this.on(\"stateChange\", this.onStateChange.bind(this));\n this.on(\"unload\", this.onUnload.bind(this));\n this.on(\"message\", this.onMessage.bind(this));\n }\n\n /** The native config, typed \u2014 single cast point for all config reads. */\n private nutConfig(): AdapterConfig {\n return this.config as unknown as AdapterConfig;\n }\n\n // Factory seams \u2014 production builds the real collaborators; the orchestration\n // unit tests (src/main.test.ts) override these fields with fakes so onReady,\n // onConnected and poll can run without sockets or a js-controller.\n private makeClient: (...args: ConstructorParameters<typeof NutClient>) => NutClient = (...args) =>\n new NutClient(...args);\n private makeStateManager: () => StateManager = () => new StateManager(this);\n\n // Single source for the {debug,warn,info} logger passed to the NUT client, the test-client\n // factory and the message router \u2014 avoids rebuilding the same wrapper at three call sites.\n private get nutLogger(): NutLogger {\n return {\n debug: (m: string) => this.log.debug(m),\n warn: (m: string) => this.log.warn(m),\n info: (m: string) => this.log.info(m),\n };\n }\n\n private async onReady(): Promise<void> {\n try {\n await I18n.init(join(this.adapterDir, \"admin\"), this);\n const config = this.nutConfig();\n this.log.debug(\n `onReady: starting (host='${config.host}', port=${JSON.stringify(config.port)}, pollInterval=${JSON.stringify(config.pollInterval)}s)`,\n );\n\n await this.setStateChangedAsync(\"info.connection\", { val: false, ack: true });\n\n const host = coerceHost(config.host);\n if (!host) {\n this.log.error(\"NUT server host is required \u2014 check adapter configuration\");\n return;\n }\n\n const port = coercePort(config.port);\n const commandTimeoutMs = coerceCommandTimeoutMs(config.commandTimeout);\n this.log.debug(`commandTimeout: raw=${JSON.stringify(config.commandTimeout)} resolved=${commandTimeoutMs}ms`);\n\n const localAddress = localAddressOf(config.networkInterface);\n\n this.client = this.makeClient(host, port, {\n localAddress,\n commandTimeout: commandTimeoutMs,\n useTls: !!config.useTls,\n tlsRejectUnauthorized: !!config.tlsRejectUnauthorized,\n // Inject the adapter-managed timers so the client's command/reconnect timeouts are\n // tracked and auto-cleared on unload (no native setTimeout leaks).\n setTimer: (cb, ms) => this.setTimeout(cb, ms),\n clearTimer: h => {\n if (h != null) {\n this.clearTimeout(h as ioBroker.Timeout);\n }\n },\n logger: this.nutLogger,\n });\n this.stateManager = this.makeStateManager();\n\n // Unified retry loop lives in the client (start): it retries the initial connect,\n // reconnects on drops, and runs the idempotent post-connect setup on every (re)connect.\n this.client.setOnConnect(() => {\n void this.onConnected().catch((err: unknown) => this.log.error(`onConnected failed: ${errText(err)}`));\n });\n this.client.setOnFatal((err: unknown) => this.onConnectFatal(err));\n this.client.start();\n } catch (err: unknown) {\n this.log.error(`onReady failed: ${errText(err)}`);\n }\n }\n\n /**\n * Idempotent post-connect setup, run on the initial connect AND every reconnect (the client's\n * single retry loop drives both): discover UPSes, (re-)authenticate, refresh command buttons,\n * poll, and arm the poll timer + subscription once. Auth failure goes yellow and stops the\n * loop via destroy(). Making \"initial == reconnect\" one path keeps the two from drifting.\n */\n private async onConnected(): Promise<void> {\n if (this.unloaded || !this.client || !this.stateManager) {\n return;\n }\n const config = this.nutConfig();\n const host = coerceHost(config.host) ?? \"\";\n const port = coercePort(config.port);\n const pollSec = coercePollIntervalSec(config.pollInterval);\n\n try {\n this.enrichedUps.clear(); // fresh connection \u2192 re-enrich enum/range metadata\n await this.discover();\n\n if (!(await this.authenticateIfConfigured(host, port))) {\n return; // auth failed \u2192 client destroyed, adapter left idle/yellow\n }\n await this.setupCommandButtons();\n\n await this.poll();\n this.armPollTimer(config.pollInterval, pollSec);\n\n if (!this.subscribed && (config.enableCommands || config.enableSetVar)) {\n await this.subscribeStatesAsync(\"*\");\n this.subscribed = true;\n }\n\n if (this.everConnected) {\n this.log.info(`Reconnected to NUT server ${host}:${port} \u2014 ${this.discoveredUps.size} UPS(es)`);\n } else {\n this.everConnected = true;\n const authStatus = this.authenticated ? \"authenticated\" : \"no credentials\";\n this.log.info(\n `NUT adapter started \u2014 ${this.discoveredUps.size} UPS(es) on ${host}:${port}, polling every ${pollSec}s (${authStatus})`,\n );\n }\n } catch (err) {\n this.log.error(`Post-connect setup failed: ${errText(err)}`);\n // Still arm the poll timer if setup failed on a live socket (e.g. a DB write during discovery\n // threw): otherwise the adapter stays connected but never polls, with no socket close to\n // trigger a reconnect. Auth failure returns earlier (client destroyed) and never reaches here,\n // so it correctly stays idle.\n this.armPollTimer(config.pollInterval, pollSec);\n }\n }\n\n /**\n * Authenticate when credentials are configured. USERNAME/PASSWORD is all that GET/LIST + SET VAR\n * + INSTCMD need; we deliberately do NOT send LOGIN per UPS (NUT permits only one LOGIN per\n * connection, so a multi-UPS server fails the second with ALREADY-LOGGED-IN, and LOGIN only\n * matters for upsmon shutdown coordination, which this adapter does not do).\n *\n * @param host NUT server host (for logging)\n * @param port NUT server port (for logging)\n * @returns true to continue setup; false when authentication failed and the client was destroyed\n */\n private async authenticateIfConfigured(host: string, port: number): Promise<boolean> {\n this.authenticated = false;\n const config = this.nutConfig();\n if (!config.username || !config.password || !this.client) {\n return true;\n }\n try {\n await this.client.authenticate(config.username, config.password);\n this.authenticated = true;\n this.log.debug(`Authenticated to NUT server ${host}:${port}`);\n return true;\n } catch (err) {\n this.log.error(`Authentication failed: ${errText(err)} \u2014 check NUT server credentials`);\n this.log.info(\n `Authentication required \u2014 adapter is idle (yellow) until the credentials are corrected; use the connection test in admin to verify them`,\n );\n this.client.destroy(); // stop the retry loop; stay alive + yellow\n await this.setStateChangedAsync(\"info.connection\", { val: false, ack: true });\n return false;\n }\n }\n\n /**\n * Create instant-command button states for every discovered UPS. Only runs when we authenticated\n * and commands are enabled; each UPS is best-effort.\n */\n private async setupCommandButtons(): Promise<void> {\n if (!this.authenticated || !this.nutConfig().enableCommands || !this.client || !this.stateManager) {\n return;\n }\n for (const ups of this.discoveredUps.keys()) {\n try {\n const commands = await this.client.listCmd(ups);\n await this.stateManager.createCommandButtons(ups, commands);\n this.log.debug(`Created ${commands.length} command buttons for ${ups}`);\n } catch (err) {\n this.log.debug(`Failed to list commands for ${ups}: ${errText(err)}`);\n }\n }\n }\n\n /**\n * Arm the periodic poll timer once. Called on the normal setup path and again from the\n * post-connect error handler, so a non-connection failure on a live socket still recovers.\n *\n * @param rawInterval Raw configured poll interval, logged for diagnostics\n * @param pollSec Resolved poll interval in seconds\n */\n private armPollTimer(rawInterval: unknown, pollSec: number): void {\n if (this.unloaded || this.pollTimer !== undefined) {\n return;\n }\n this.log.debug(`pollInterval: raw=${JSON.stringify(rawInterval)} resolved=${pollSec}s`);\n this.pollTimer = this.setInterval(() => {\n void this.poll();\n }, pollSec * 1000);\n }\n\n /**\n * The persistent connection failed fatally (TLS misconfiguration). The client already stopped\n * retrying; stay alive + yellow so the admin connection-test button remains usable.\n *\n * @param err The fatal connect/STARTTLS error\n */\n private onConnectFatal(err: unknown): void {\n const config = this.nutConfig();\n const host = coerceHost(config.host) ?? \"\";\n const port = coercePort(config.port);\n this.log.error(\n `TLS connection to NUT server ${host}:${port} failed: ${errText(err)} \u2014 verify the server offers STARTTLS and check the certificate setting`,\n );\n this.client?.destroy();\n void this.setStateChangedAsync(\"info.connection\", { val: false, ack: true }).catch(() => {});\n }\n\n private async discover(): Promise<void> {\n if (!this.client || !this.stateManager) {\n return;\n }\n const upsList = await this.client.listUps();\n this.log.debug(`Discovered ${upsList.length} UPS(es): ${upsList.map(u => u.name).join(\", \")}`);\n\n this.discoveredUps.clear();\n for (const ups of upsList) {\n this.discoveredUps.set(ups.name, ups);\n await this.stateManager.ensureUpsDevice(ups.name, ups.description);\n }\n\n const knownNames = new Set(this.discoveredUps.keys());\n await this.stateManager.cleanupRemovedUps(knownNames);\n await this.stateManager.cleanupLegacyObjects(knownNames);\n\n // Prune the in-memory per-UPS markers alongside the object cleanup \u2014 a UPS that\n // disappears and later re-appears must start fresh (a stale failedUps entry would\n // demote its first real error to debug; a stale enrichedUps entry would skip the\n // enum/range enrichment).\n for (const name of this.failedUps) {\n if (!knownNames.has(name)) {\n this.failedUps.delete(name);\n }\n }\n for (const name of this.enrichedUps) {\n if (!knownNames.has(name)) {\n this.enrichedUps.delete(name);\n }\n }\n }\n\n private classifyError(err: unknown): string {\n if (err instanceof NutError) {\n return err.code;\n }\n if (!(err instanceof Error)) {\n return \"UNKNOWN\";\n }\n const code = (err as NodeJS.ErrnoException).code;\n if (\n code === \"ENOTFOUND\" ||\n code === \"ECONNREFUSED\" ||\n code === \"ECONNRESET\" ||\n code === \"ENETUNREACH\" ||\n code === \"EHOSTUNREACH\" ||\n code === \"EAI_AGAIN\"\n ) {\n return \"NETWORK\";\n }\n if (code === \"ETIMEDOUT\" || err.message.includes(\"timed out\")) {\n return \"TIMEOUT\";\n }\n return code || \"UNKNOWN\";\n }\n\n private async poll(): Promise<void> {\n if (this.isPolling) {\n this.log.debug(\"Skipping poll \u2014 previous poll still running\");\n return;\n }\n if (!this.client || !this.stateManager) {\n return;\n }\n\n this.log.debug(`poll: starting (lastErrorCode='${this.lastErrorCode}', upsCount=${this.discoveredUps.size})`);\n\n this.isPolling = true;\n try {\n for (const upsName of this.discoveredUps.keys()) {\n try {\n // Only query LIST RW when SET VAR is enabled \u2014 otherwise the variables would be marked\n // writable (write: true) in the admin object tree while a write is silently blocked, and\n // querying is pointless. With SET VAR off every variable stays read-only.\n const [variables, rwVars] = await Promise.all([\n this.client.listVar(upsName),\n this.nutConfig().enableSetVar\n ? this.client.listRw(upsName).catch((err: unknown) => {\n this.log.debug(`LIST RW ${upsName} failed (non-critical): ${errText(err)}`);\n return [] as NutVariable[];\n })\n : Promise.resolve<NutVariable[]>([]),\n ]);\n\n const rwNames = new Set(rwVars.map(v => v.name));\n await this.stateManager.updateVariables(upsName, variables, rwNames);\n\n const upsDesc = this.discoveredUps.get(upsName);\n await this.stateManager.updateDeviceName(upsName, upsDesc?.description ?? \"\", variables);\n\n const statusVar = variables.find(v => v.name === \"ups.status\");\n if (statusVar) {\n // Pass battery.charger.status so charging/discharging fill in even on UPSes that\n // report it instead of the CHRG/DISCHRG status flags (e.g. Eaton Ellipse ECO).\n const chargerStatus = variables.find(v => v.name === \"battery.charger.status\")?.value;\n await this.stateManager.updateStatusFlags(upsName, statusVar.value, chargerStatus);\n }\n\n await this.enrichWritableVars(upsName, rwVars);\n\n await this.setStateChangedAsync(`${upsName}.info.reachable`, { val: true, ack: true });\n\n if (this.failedUps.has(upsName)) {\n this.log.info(`UPS '${upsName}' recovered`);\n this.failedUps.delete(upsName);\n }\n } catch (err) {\n await this.setStateChangedAsync(`${upsName}.info.reachable`, { val: false, ack: true });\n\n const msg = `Failed to poll UPS '${upsName}': ${errText(err)}`;\n if (this.failedUps.has(upsName)) {\n this.log.debug(msg);\n } else {\n const isDataStale = err instanceof NutError && err.code === \"DATA-STALE\";\n if (isDataStale) {\n this.log.warn(`UPS '${upsName}': driver reports stale data \u2014 keeping existing states`);\n } else {\n this.log.warn(msg);\n }\n this.failedUps.add(upsName);\n }\n }\n }\n\n // Reflect the real TCP/NUT-server connection, not \"the poll loop ran\": per-UPS errors are\n // caught inside the loop, so an unconditional `true` here would show green even while the\n // connection is down (poll keeps firing during the reconnect backoff). Gate on the client.\n await this.setStateChangedAsync(\"info.connection\", { val: this.client?.isConnected ?? false, ack: true });\n\n if (this.lastErrorCode) {\n this.log.info(\"Connection restored\");\n this.lastErrorCode = \"\";\n }\n } catch (err) {\n const errMsg = errText(err);\n const errorCode = this.classifyError(err);\n const isRepeat = errorCode === this.lastErrorCode;\n this.lastErrorCode = errorCode;\n\n if (isRepeat) {\n this.log.debug(`Poll failed (ongoing): ${errMsg}`);\n } else if (errorCode === \"NETWORK\") {\n this.log.warn(\"Cannot reach NUT server \u2014 will keep retrying\");\n } else {\n this.log.error(`Poll failed: ${errMsg}`);\n }\n\n await this.setStateChangedAsync(\"info.connection\", { val: false, ack: true });\n } finally {\n this.isPolling = false;\n }\n }\n\n /**\n * Enrich writable variables with ENUM (common.states) and RANGE (min/max) metadata, once per UPS\n * per connection (guarded by enrichedUps). Each query is best-effort \u2014 a driver that does not\n * support LIST ENUM/RANGE just logs at debug.\n *\n * @param upsName UPS identifier\n * @param rwVars Writable variables from LIST RW\n */\n private async enrichWritableVars(upsName: string, rwVars: NutVariable[]): Promise<void> {\n if (!this.client || !this.stateManager || this.enrichedUps.has(upsName) || rwVars.length === 0) {\n return;\n }\n for (const rw of rwVars) {\n const stateId = nutVarToStateId(upsName, rw.name);\n // A writable yes/no var is a boolean state (detectType \u2192 boolean only via parseYesNo). Its\n // LIST ENUM yes/no must not become common.states \u2014 a string-keyed {yes,no} map is meaningless\n // on a boolean \u2014 so skip the enum round-trip entirely for booleans. RANGE stays (harmless: a\n // boolean has none). Multi-value string/number enums are unaffected.\n const isBoolean = detectType(rw.name, rw.value, true).type === \"boolean\";\n if (!isBoolean) {\n try {\n const enumVals = await this.client.listEnum(upsName, rw.name);\n if (enumVals.length > 0) {\n const states: Record<string, string> = {};\n for (const v of enumVals) {\n states[v] = v;\n }\n await this.stateManager.enrichStateMetadata(stateId, { states });\n }\n } catch (err: unknown) {\n this.log.debug(`LIST ENUM ${upsName} ${rw.name}: not supported (${errText(err)})`);\n }\n }\n try {\n const ranges = await this.client.listRange(upsName, rw.name);\n if (ranges.length > 0) {\n const min = parseDecimal(ranges[0].min);\n const max = parseDecimal(ranges[0].max);\n const patch: Partial<Record<\"min\" | \"max\", number>> = {};\n if (!Number.isNaN(min)) {\n patch.min = min;\n }\n if (!Number.isNaN(max)) {\n patch.max = max;\n }\n await this.stateManager.enrichStateMetadata(stateId, patch);\n }\n } catch (err: unknown) {\n this.log.debug(`LIST RANGE ${upsName} ${rw.name}: not supported (${errText(err)})`);\n }\n }\n this.enrichedUps.add(upsName);\n }\n\n private async onStateChange(id: string, state: ioBroker.State | null | undefined): Promise<void> {\n try {\n if (!state || state.ack) {\n return;\n }\n const config = this.nutConfig();\n const localId = id.replace(`${this.namespace}.`, \"\");\n this.log.debug(`onStateChange: ${localId} val=${JSON.stringify(state.val)}`);\n\n if (!this.client) {\n this.log.debug(`onStateChange: ignoring ${localId} \u2014 no client connection`);\n return;\n }\n\n const parts = localId.split(\".\");\n\n if (parts.length < 3) {\n this.log.debug(`onStateChange: unexpected id structure '${localId}', ignoring`);\n return;\n }\n\n const upsName = parts[0];\n if (!this.discoveredUps.has(upsName)) {\n this.log.debug(`onStateChange: unknown UPS '${upsName}', ignoring`);\n return;\n }\n\n if (parts[1] === \"commands\") {\n if (!config.enableCommands) {\n this.log.warn(`Command blocked \u2014 enableCommands is disabled: ${localId}`);\n return;\n }\n const cmdName = this.stateManager?.nutNameForState(localId) ?? parts.slice(2).join(\".\").replace(/-/g, \".\");\n this.log.debug(`INSTCMD ${upsName} ${cmdName}`);\n try {\n await this.client.instCmd(upsName, cmdName);\n this.log.info(`Command executed: ${cmdName} on ${upsName}`);\n } catch (err) {\n this.log.error(`Command failed: ${cmdName} on ${upsName} \u2014 ${errText(err)}`);\n }\n await this.setState(id, { val: false, ack: true });\n return;\n }\n\n if (!config.enableSetVar) {\n this.log.warn(`SET VAR blocked \u2014 enableSetVar is disabled: ${localId}`);\n return;\n }\n\n const varName =\n this.stateManager?.nutNameForState(localId) ?? `${parts[1]}.${parts.slice(2).join(\".\").replace(/-/g, \".\")}`;\n // A writable yes/no variable (ups.start.auto/.battery/.reboot, battery.protection) is stored\n // as a boolean state (detectType \u2192 boolean only via parseYesNo, so boolean \u27FA the NUT var\n // accepts yes/no). Translate it back to the token NUT expects \u2014 String(true) = \"true\" would\n // be rejected with INVALID-VALUE/SET-FAILED. Numbers/enum strings write verbatim.\n const value = typeof state.val === \"boolean\" ? (state.val ? \"yes\" : \"no\") : String(state.val);\n this.log.debug(`SET VAR ${upsName} ${varName} \"${value}\"`);\n try {\n await this.client.setVar(upsName, varName, value);\n await this.setState(id, { val: state.val, ack: true });\n this.log.info(`Variable set: ${varName} = \"${value}\" on ${upsName}`);\n } catch (err) {\n this.log.error(`SET VAR failed: ${varName} on ${upsName} \u2014 ${errText(err)}`);\n }\n } catch (err: unknown) {\n this.log.error(`onStateChange failed: ${errText(err)}`);\n }\n }\n\n private async onMessage(obj: ioBroker.Message): Promise<void> {\n try {\n await dispatchMessage(obj, {\n log: this.nutLogger,\n sendTo: this.sendTo.bind(this),\n createTestClient: makeTestClientFactory(NutClient, this.nutLogger),\n onTestClientCreated: client => {\n this.testClients.add(client);\n },\n onTestClientDone: client => {\n this.testClients.delete(client);\n },\n });\n } catch (err: unknown) {\n this.log.error(`onMessage failed: ${errText(err)}`);\n }\n }\n\n private onUnload(callback: () => void): void {\n try {\n this.unloaded = true;\n if (this.pollTimer) {\n this.clearInterval(this.pollTimer);\n this.pollTimer = undefined;\n }\n // The client owns its reconnect timer (managed via this.setTimeout) \u2014 destroy()/shutdown()\n // below clears it.\n // Best-effort graceful LOGOUT when logged in; a hard close otherwise.\n if (this.authenticated) {\n this.client?.shutdown();\n } else {\n this.client?.destroy();\n }\n for (const tc of this.testClients) {\n tc.destroy();\n }\n this.testClients.clear();\n void this.setState(\"info.connection\", { val: false, ack: true }).catch(() => {});\n } catch (err) {\n this.log.debug(`onUnload error (ignored): ${errText(err)}`);\n }\n callback();\n }\n}\n\nif (require.main !== module) {\n module.exports = (options: Partial<utils.AdapterOptions> | undefined) => new NutAdapter(options);\n} else {\n (() => new NutAdapter())();\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAAuB;AACvB,0BAAqB;AACrB,uBAAqB;AACrB,oBAQO;AACP,4BAAuD;AACvD,wBAAoC;AACpC,2BAA8C;AAC9C,2BAA2B;AAOpB,MAAM,mBAAmB,MAAM,QAAQ;AAAA,EACpC,SAA2B;AAAA,EAC3B,eAAoC;AAAA,EACpC,YAA2C;AAAA,EAC3C,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,YAAY,oBAAI,IAAY;AAAA,EAC5B,gBAAgB,oBAAI,IAAqB;AAAA,EACzC,gBAAgB;AAAA,EAChB,cAAc,oBAAI,IAAY;AAAA,EAC9B,cAAc,oBAAI,IAAe;AAAA,EACjC,aAAa;AAAA,EACb,WAAW;AAAA,EACX,gBAAgB;AAAA;AAAA,EAGxB,YAAY,UAAyC,CAAC,GAAG;AACvD,UAAM,EAAE,GAAG,SAAS,MAAM,OAAO,CAAC;AAClC,SAAK,GAAG,SAAS,KAAK,QAAQ,KAAK,IAAI,CAAC;AACxC,SAAK,GAAG,eAAe,KAAK,cAAc,KAAK,IAAI,CAAC;AACpD,SAAK,GAAG,UAAU,KAAK,SAAS,KAAK,IAAI,CAAC;AAC1C,SAAK,GAAG,WAAW,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,EAC9C;AAAA;AAAA,EAGQ,YAA2B;AACjC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKQ,aAA8E,IAAI,SACxF,IAAI,4BAAU,GAAG,IAAI;AAAA,EACf,mBAAuC,MAAM,IAAI,kCAAa,IAAI;AAAA;AAAA;AAAA,EAI1E,IAAY,YAAuB;AACjC,WAAO;AAAA,MACL,OAAO,CAAC,MAAc,KAAK,IAAI,MAAM,CAAC;AAAA,MACtC,MAAM,CAAC,MAAc,KAAK,IAAI,KAAK,CAAC;AAAA,MACpC,MAAM,CAAC,MAAc,KAAK,IAAI,KAAK,CAAC;AAAA,IACtC;AAAA,EACF;AAAA,EAEA,MAAc,UAAyB;AACrC,QAAI;AACF,YAAM,yBAAK,SAAK,uBAAK,KAAK,YAAY,OAAO,GAAG,IAAI;AACpD,YAAM,SAAS,KAAK,UAAU;AAC9B,WAAK,IAAI;AAAA,QACP,4BAA4B,OAAO,IAAI,WAAW,KAAK,UAAU,OAAO,IAAI,CAAC,kBAAkB,KAAK,UAAU,OAAO,YAAY,CAAC;AAAA,MACpI;AAEA,YAAM,KAAK,qBAAqB,mBAAmB,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC;AAE5E,YAAM,WAAO,0BAAW,OAAO,IAAI;AACnC,UAAI,CAAC,MAAM;AACT,aAAK,IAAI,MAAM,gEAA2D;AAC1E;AAAA,MACF;AAEA,YAAM,WAAO,0BAAW,OAAO,IAAI;AACnC,YAAM,uBAAmB,sCAAuB,OAAO,cAAc;AACrE,WAAK,IAAI,MAAM,uBAAuB,KAAK,UAAU,OAAO,cAAc,CAAC,aAAa,gBAAgB,IAAI;AAE5G,YAAM,mBAAe,8BAAe,OAAO,gBAAgB;AAE3D,WAAK,SAAS,KAAK,WAAW,MAAM,MAAM;AAAA,QACxC;AAAA,QACA,gBAAgB;AAAA,QAChB,QAAQ,CAAC,CAAC,OAAO;AAAA,QACjB,uBAAuB,CAAC,CAAC,OAAO;AAAA;AAAA;AAAA,QAGhC,UAAU,CAAC,IAAI,OAAO,KAAK,WAAW,IAAI,EAAE;AAAA,QAC5C,YAAY,OAAK;AACf,cAAI,KAAK,MAAM;AACb,iBAAK,aAAa,CAAqB;AAAA,UACzC;AAAA,QACF;AAAA,QACA,QAAQ,KAAK;AAAA,MACf,CAAC;AACD,WAAK,eAAe,KAAK,iBAAiB;AAI1C,WAAK,OAAO,aAAa,MAAM;AAC7B,aAAK,KAAK,YAAY,EAAE,MAAM,CAAC,QAAiB,KAAK,IAAI,MAAM,2BAAuB,uBAAQ,GAAG,CAAC,EAAE,CAAC;AAAA,MACvG,CAAC;AACD,WAAK,OAAO,WAAW,CAAC,QAAiB,KAAK,eAAe,GAAG,CAAC;AACjE,WAAK,OAAO,MAAM;AAAA,IACpB,SAAS,KAAc;AACrB,WAAK,IAAI,MAAM,uBAAmB,uBAAQ,GAAG,CAAC,EAAE;AAAA,IAClD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,cAA6B;AA7H7C;AA8HI,QAAI,KAAK,YAAY,CAAC,KAAK,UAAU,CAAC,KAAK,cAAc;AACvD;AAAA,IACF;AACA,UAAM,SAAS,KAAK,UAAU;AAC9B,UAAM,QAAO,mCAAW,OAAO,IAAI,MAAtB,YAA2B;AACxC,UAAM,WAAO,0BAAW,OAAO,IAAI;AACnC,UAAM,cAAU,qCAAsB,OAAO,YAAY;AAEzD,QAAI;AACF,WAAK,YAAY,MAAM;AACvB,YAAM,KAAK,SAAS;AAEpB,UAAI,CAAE,MAAM,KAAK,yBAAyB,MAAM,IAAI,GAAI;AACtD;AAAA,MACF;AACA,YAAM,KAAK,oBAAoB;AAE/B,YAAM,KAAK,KAAK;AAChB,WAAK,aAAa,OAAO,cAAc,OAAO;AAE9C,UAAI,CAAC,KAAK,eAAe,OAAO,kBAAkB,OAAO,eAAe;AACtE,cAAM,KAAK,qBAAqB,GAAG;AACnC,aAAK,aAAa;AAAA,MACpB;AAEA,UAAI,KAAK,eAAe;AACtB,aAAK,IAAI,KAAK,6BAA6B,IAAI,IAAI,IAAI,WAAM,KAAK,cAAc,IAAI,UAAU;AAAA,MAChG,OAAO;AACL,aAAK,gBAAgB;AACrB,cAAM,aAAa,KAAK,gBAAgB,kBAAkB;AAC1D,aAAK,IAAI;AAAA,UACP,8BAAyB,KAAK,cAAc,IAAI,eAAe,IAAI,IAAI,IAAI,mBAAmB,OAAO,MAAM,UAAU;AAAA,QACvH;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,WAAK,IAAI,MAAM,kCAA8B,uBAAQ,GAAG,CAAC,EAAE;AAK3D,WAAK,aAAa,OAAO,cAAc,OAAO;AAAA,IAChD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAc,yBAAyB,MAAc,MAAgC;AACnF,SAAK,gBAAgB;AACrB,UAAM,SAAS,KAAK,UAAU;AAC9B,QAAI,CAAC,OAAO,YAAY,CAAC,OAAO,YAAY,CAAC,KAAK,QAAQ;AACxD,aAAO;AAAA,IACT;AACA,QAAI;AACF,YAAM,KAAK,OAAO,aAAa,OAAO,UAAU,OAAO,QAAQ;AAC/D,WAAK,gBAAgB;AACrB,WAAK,IAAI,MAAM,+BAA+B,IAAI,IAAI,IAAI,EAAE;AAC5D,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,WAAK,IAAI,MAAM,8BAA0B,uBAAQ,GAAG,CAAC,sCAAiC;AACtF,WAAK,IAAI;AAAA,QACP;AAAA,MACF;AACA,WAAK,OAAO,QAAQ;AACpB,YAAM,KAAK,qBAAqB,mBAAmB,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC;AAC5E,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,sBAAqC;AACjD,QAAI,CAAC,KAAK,iBAAiB,CAAC,KAAK,UAAU,EAAE,kBAAkB,CAAC,KAAK,UAAU,CAAC,KAAK,cAAc;AACjG;AAAA,IACF;AACA,eAAW,OAAO,KAAK,cAAc,KAAK,GAAG;AAC3C,UAAI;AACF,cAAM,WAAW,MAAM,KAAK,OAAO,QAAQ,GAAG;AAC9C,cAAM,KAAK,aAAa,qBAAqB,KAAK,QAAQ;AAC1D,aAAK,IAAI,MAAM,WAAW,SAAS,MAAM,wBAAwB,GAAG,EAAE;AAAA,MACxE,SAAS,KAAK;AACZ,aAAK,IAAI,MAAM,+BAA+B,GAAG,SAAK,uBAAQ,GAAG,CAAC,EAAE;AAAA,MACtE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,aAAa,aAAsB,SAAuB;AAChE,QAAI,KAAK,YAAY,KAAK,cAAc,QAAW;AACjD;AAAA,IACF;AACA,SAAK,IAAI,MAAM,qBAAqB,KAAK,UAAU,WAAW,CAAC,aAAa,OAAO,GAAG;AACtF,SAAK,YAAY,KAAK,YAAY,MAAM;AACtC,WAAK,KAAK,KAAK;AAAA,IACjB,GAAG,UAAU,GAAI;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,eAAe,KAAoB;AApP7C;AAqPI,UAAM,SAAS,KAAK,UAAU;AAC9B,UAAM,QAAO,mCAAW,OAAO,IAAI,MAAtB,YAA2B;AACxC,UAAM,WAAO,0BAAW,OAAO,IAAI;AACnC,SAAK,IAAI;AAAA,MACP,gCAAgC,IAAI,IAAI,IAAI,gBAAY,uBAAQ,GAAG,CAAC;AAAA,IACtE;AACA,eAAK,WAAL,mBAAa;AACb,SAAK,KAAK,qBAAqB,mBAAmB,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAC7F;AAAA,EAEA,MAAc,WAA0B;AACtC,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,cAAc;AACtC;AAAA,IACF;AACA,UAAM,UAAU,MAAM,KAAK,OAAO,QAAQ;AAC1C,SAAK,IAAI,MAAM,cAAc,QAAQ,MAAM,aAAa,QAAQ,IAAI,OAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC,EAAE;AAE7F,SAAK,cAAc,MAAM;AACzB,eAAW,OAAO,SAAS;AACzB,WAAK,cAAc,IAAI,IAAI,MAAM,GAAG;AACpC,YAAM,KAAK,aAAa,gBAAgB,IAAI,MAAM,IAAI,WAAW;AAAA,IACnE;AAEA,UAAM,aAAa,IAAI,IAAI,KAAK,cAAc,KAAK,CAAC;AACpD,UAAM,KAAK,aAAa,kBAAkB,UAAU;AACpD,UAAM,KAAK,aAAa,qBAAqB,UAAU;AAMvD,eAAW,QAAQ,KAAK,WAAW;AACjC,UAAI,CAAC,WAAW,IAAI,IAAI,GAAG;AACzB,aAAK,UAAU,OAAO,IAAI;AAAA,MAC5B;AAAA,IACF;AACA,eAAW,QAAQ,KAAK,aAAa;AACnC,UAAI,CAAC,WAAW,IAAI,IAAI,GAAG;AACzB,aAAK,YAAY,OAAO,IAAI;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,cAAc,KAAsB;AAC1C,QAAI,eAAe,4BAAU;AAC3B,aAAO,IAAI;AAAA,IACb;AACA,QAAI,EAAE,eAAe,QAAQ;AAC3B,aAAO;AAAA,IACT;AACA,UAAM,OAAQ,IAA8B;AAC5C,QACE,SAAS,eACT,SAAS,kBACT,SAAS,gBACT,SAAS,iBACT,SAAS,kBACT,SAAS,aACT;AACA,aAAO;AAAA,IACT;AACA,QAAI,SAAS,eAAe,IAAI,QAAQ,SAAS,WAAW,GAAG;AAC7D,aAAO;AAAA,IACT;AACA,WAAO,QAAQ;AAAA,EACjB;AAAA,EAEA,MAAc,OAAsB;AAxTtC;AAyTI,QAAI,KAAK,WAAW;AAClB,WAAK,IAAI,MAAM,kDAA6C;AAC5D;AAAA,IACF;AACA,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,cAAc;AACtC;AAAA,IACF;AAEA,SAAK,IAAI,MAAM,kCAAkC,KAAK,aAAa,eAAe,KAAK,cAAc,IAAI,GAAG;AAE5G,SAAK,YAAY;AACjB,QAAI;AACF,iBAAW,WAAW,KAAK,cAAc,KAAK,GAAG;AAC/C,YAAI;AAIF,gBAAM,CAAC,WAAW,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,YAC5C,KAAK,OAAO,QAAQ,OAAO;AAAA,YAC3B,KAAK,UAAU,EAAE,eACb,KAAK,OAAO,OAAO,OAAO,EAAE,MAAM,CAAC,QAAiB;AAClD,mBAAK,IAAI,MAAM,WAAW,OAAO,+BAA2B,uBAAQ,GAAG,CAAC,EAAE;AAC1E,qBAAO,CAAC;AAAA,YACV,CAAC,IACD,QAAQ,QAAuB,CAAC,CAAC;AAAA,UACvC,CAAC;AAED,gBAAM,UAAU,IAAI,IAAI,OAAO,IAAI,OAAK,EAAE,IAAI,CAAC;AAC/C,gBAAM,KAAK,aAAa,gBAAgB,SAAS,WAAW,OAAO;AAEnE,gBAAM,UAAU,KAAK,cAAc,IAAI,OAAO;AAC9C,gBAAM,KAAK,aAAa,iBAAiB,UAAS,wCAAS,gBAAT,YAAwB,IAAI,SAAS;AAEvF,gBAAM,YAAY,UAAU,KAAK,OAAK,EAAE,SAAS,YAAY;AAC7D,cAAI,WAAW;AAGb,kBAAM,iBAAgB,eAAU,KAAK,OAAK,EAAE,SAAS,wBAAwB,MAAvD,mBAA0D;AAChF,kBAAM,KAAK,aAAa,kBAAkB,SAAS,UAAU,OAAO,aAAa;AAAA,UACnF;AAEA,gBAAM,KAAK,mBAAmB,SAAS,MAAM;AAE7C,gBAAM,KAAK,qBAAqB,GAAG,OAAO,mBAAmB,EAAE,KAAK,MAAM,KAAK,KAAK,CAAC;AAErF,cAAI,KAAK,UAAU,IAAI,OAAO,GAAG;AAC/B,iBAAK,IAAI,KAAK,QAAQ,OAAO,aAAa;AAC1C,iBAAK,UAAU,OAAO,OAAO;AAAA,UAC/B;AAAA,QACF,SAAS,KAAK;AACZ,gBAAM,KAAK,qBAAqB,GAAG,OAAO,mBAAmB,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC;AAEtF,gBAAM,MAAM,uBAAuB,OAAO,UAAM,uBAAQ,GAAG,CAAC;AAC5D,cAAI,KAAK,UAAU,IAAI,OAAO,GAAG;AAC/B,iBAAK,IAAI,MAAM,GAAG;AAAA,UACpB,OAAO;AACL,kBAAM,cAAc,eAAe,8BAAY,IAAI,SAAS;AAC5D,gBAAI,aAAa;AACf,mBAAK,IAAI,KAAK,QAAQ,OAAO,6DAAwD;AAAA,YACvF,OAAO;AACL,mBAAK,IAAI,KAAK,GAAG;AAAA,YACnB;AACA,iBAAK,UAAU,IAAI,OAAO;AAAA,UAC5B;AAAA,QACF;AAAA,MACF;AAKA,YAAM,KAAK,qBAAqB,mBAAmB,EAAE,MAAK,gBAAK,WAAL,mBAAa,gBAAb,YAA4B,OAAO,KAAK,KAAK,CAAC;AAExG,UAAI,KAAK,eAAe;AACtB,aAAK,IAAI,KAAK,qBAAqB;AACnC,aAAK,gBAAgB;AAAA,MACvB;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,aAAS,uBAAQ,GAAG;AAC1B,YAAM,YAAY,KAAK,cAAc,GAAG;AACxC,YAAM,WAAW,cAAc,KAAK;AACpC,WAAK,gBAAgB;AAErB,UAAI,UAAU;AACZ,aAAK,IAAI,MAAM,0BAA0B,MAAM,EAAE;AAAA,MACnD,WAAW,cAAc,WAAW;AAClC,aAAK,IAAI,KAAK,mDAA8C;AAAA,MAC9D,OAAO;AACL,aAAK,IAAI,MAAM,gBAAgB,MAAM,EAAE;AAAA,MACzC;AAEA,YAAM,KAAK,qBAAqB,mBAAmB,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC;AAAA,IAC9E,UAAE;AACA,WAAK,YAAY;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,mBAAmB,SAAiB,QAAsC;AACtF,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,gBAAgB,KAAK,YAAY,IAAI,OAAO,KAAK,OAAO,WAAW,GAAG;AAC9F;AAAA,IACF;AACA,eAAW,MAAM,QAAQ;AACvB,YAAM,cAAU,sCAAgB,SAAS,GAAG,IAAI;AAKhD,YAAM,gBAAY,iCAAW,GAAG,MAAM,GAAG,OAAO,IAAI,EAAE,SAAS;AAC/D,UAAI,CAAC,WAAW;AACd,YAAI;AACF,gBAAM,WAAW,MAAM,KAAK,OAAO,SAAS,SAAS,GAAG,IAAI;AAC5D,cAAI,SAAS,SAAS,GAAG;AACvB,kBAAM,SAAiC,CAAC;AACxC,uBAAW,KAAK,UAAU;AACxB,qBAAO,CAAC,IAAI;AAAA,YACd;AACA,kBAAM,KAAK,aAAa,oBAAoB,SAAS,EAAE,OAAO,CAAC;AAAA,UACjE;AAAA,QACF,SAAS,KAAc;AACrB,eAAK,IAAI,MAAM,aAAa,OAAO,IAAI,GAAG,IAAI,wBAAoB,uBAAQ,GAAG,CAAC,GAAG;AAAA,QACnF;AAAA,MACF;AACA,UAAI;AACF,cAAM,SAAS,MAAM,KAAK,OAAO,UAAU,SAAS,GAAG,IAAI;AAC3D,YAAI,OAAO,SAAS,GAAG;AACrB,gBAAM,UAAM,4BAAa,OAAO,CAAC,EAAE,GAAG;AACtC,gBAAM,UAAM,4BAAa,OAAO,CAAC,EAAE,GAAG;AACtC,gBAAM,QAAgD,CAAC;AACvD,cAAI,CAAC,OAAO,MAAM,GAAG,GAAG;AACtB,kBAAM,MAAM;AAAA,UACd;AACA,cAAI,CAAC,OAAO,MAAM,GAAG,GAAG;AACtB,kBAAM,MAAM;AAAA,UACd;AACA,gBAAM,KAAK,aAAa,oBAAoB,SAAS,KAAK;AAAA,QAC5D;AAAA,MACF,SAAS,KAAc;AACrB,aAAK,IAAI,MAAM,cAAc,OAAO,IAAI,GAAG,IAAI,wBAAoB,uBAAQ,GAAG,CAAC,GAAG;AAAA,MACpF;AAAA,IACF;AACA,SAAK,YAAY,IAAI,OAAO;AAAA,EAC9B;AAAA,EAEA,MAAc,cAAc,IAAY,OAAyD;AA/cnG;AAgdI,QAAI;AACF,UAAI,CAAC,SAAS,MAAM,KAAK;AACvB;AAAA,MACF;AACA,YAAM,SAAS,KAAK,UAAU;AAC9B,YAAM,UAAU,GAAG,QAAQ,GAAG,KAAK,SAAS,KAAK,EAAE;AACnD,WAAK,IAAI,MAAM,kBAAkB,OAAO,QAAQ,KAAK,UAAU,MAAM,GAAG,CAAC,EAAE;AAE3E,UAAI,CAAC,KAAK,QAAQ;AAChB,aAAK,IAAI,MAAM,2BAA2B,OAAO,8BAAyB;AAC1E;AAAA,MACF;AAEA,YAAM,QAAQ,QAAQ,MAAM,GAAG;AAE/B,UAAI,MAAM,SAAS,GAAG;AACpB,aAAK,IAAI,MAAM,2CAA2C,OAAO,aAAa;AAC9E;AAAA,MACF;AAEA,YAAM,UAAU,MAAM,CAAC;AACvB,UAAI,CAAC,KAAK,cAAc,IAAI,OAAO,GAAG;AACpC,aAAK,IAAI,MAAM,+BAA+B,OAAO,aAAa;AAClE;AAAA,MACF;AAEA,UAAI,MAAM,CAAC,MAAM,YAAY;AAC3B,YAAI,CAAC,OAAO,gBAAgB;AAC1B,eAAK,IAAI,KAAK,sDAAiD,OAAO,EAAE;AACxE;AAAA,QACF;AACA,cAAM,WAAU,gBAAK,iBAAL,mBAAmB,gBAAgB,aAAnC,YAA+C,MAAM,MAAM,CAAC,EAAE,KAAK,GAAG,EAAE,QAAQ,MAAM,GAAG;AACzG,aAAK,IAAI,MAAM,WAAW,OAAO,IAAI,OAAO,EAAE;AAC9C,YAAI;AACF,gBAAM,KAAK,OAAO,QAAQ,SAAS,OAAO;AAC1C,eAAK,IAAI,KAAK,qBAAqB,OAAO,OAAO,OAAO,EAAE;AAAA,QAC5D,SAAS,KAAK;AACZ,eAAK,IAAI,MAAM,mBAAmB,OAAO,OAAO,OAAO,eAAM,uBAAQ,GAAG,CAAC,EAAE;AAAA,QAC7E;AACA,cAAM,KAAK,SAAS,IAAI,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC;AACjD;AAAA,MACF;AAEA,UAAI,CAAC,OAAO,cAAc;AACxB,aAAK,IAAI,KAAK,oDAA+C,OAAO,EAAE;AACtE;AAAA,MACF;AAEA,YAAM,WACJ,gBAAK,iBAAL,mBAAmB,gBAAgB,aAAnC,YAA+C,GAAG,MAAM,CAAC,CAAC,IAAI,MAAM,MAAM,CAAC,EAAE,KAAK,GAAG,EAAE,QAAQ,MAAM,GAAG,CAAC;AAK3G,YAAM,QAAQ,OAAO,MAAM,QAAQ,YAAa,MAAM,MAAM,QAAQ,OAAQ,OAAO,MAAM,GAAG;AAC5F,WAAK,IAAI,MAAM,WAAW,OAAO,IAAI,OAAO,KAAK,KAAK,GAAG;AACzD,UAAI;AACF,cAAM,KAAK,OAAO,OAAO,SAAS,SAAS,KAAK;AAChD,cAAM,KAAK,SAAS,IAAI,EAAE,KAAK,MAAM,KAAK,KAAK,KAAK,CAAC;AACrD,aAAK,IAAI,KAAK,iBAAiB,OAAO,OAAO,KAAK,QAAQ,OAAO,EAAE;AAAA,MACrE,SAAS,KAAK;AACZ,aAAK,IAAI,MAAM,mBAAmB,OAAO,OAAO,OAAO,eAAM,uBAAQ,GAAG,CAAC,EAAE;AAAA,MAC7E;AAAA,IACF,SAAS,KAAc;AACrB,WAAK,IAAI,MAAM,6BAAyB,uBAAQ,GAAG,CAAC,EAAE;AAAA,IACxD;AAAA,EACF;AAAA,EAEA,MAAc,UAAU,KAAsC;AAC5D,QAAI;AACF,gBAAM,uCAAgB,KAAK;AAAA,QACzB,KAAK,KAAK;AAAA,QACV,QAAQ,KAAK,OAAO,KAAK,IAAI;AAAA,QAC7B,sBAAkB,6CAAsB,6BAAW,KAAK,SAAS;AAAA,QACjE,qBAAqB,YAAU;AAC7B,eAAK,YAAY,IAAI,MAAM;AAAA,QAC7B;AAAA,QACA,kBAAkB,YAAU;AAC1B,eAAK,YAAY,OAAO,MAAM;AAAA,QAChC;AAAA,MACF,CAAC;AAAA,IACH,SAAS,KAAc;AACrB,WAAK,IAAI,MAAM,yBAAqB,uBAAQ,GAAG,CAAC,EAAE;AAAA,IACpD;AAAA,EACF;AAAA,EAEQ,SAAS,UAA4B;AAtiB/C;AAuiBI,QAAI;AACF,WAAK,WAAW;AAChB,UAAI,KAAK,WAAW;AAClB,aAAK,cAAc,KAAK,SAAS;AACjC,aAAK,YAAY;AAAA,MACnB;AAIA,UAAI,KAAK,eAAe;AACtB,mBAAK,WAAL,mBAAa;AAAA,MACf,OAAO;AACL,mBAAK,WAAL,mBAAa;AAAA,MACf;AACA,iBAAW,MAAM,KAAK,aAAa;AACjC,WAAG,QAAQ;AAAA,MACb;AACA,WAAK,YAAY,MAAM;AACvB,WAAK,KAAK,SAAS,mBAAmB,EAAE,KAAK,OAAO,KAAK,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IACjF,SAAS,KAAK;AACZ,WAAK,IAAI,MAAM,iCAA6B,uBAAQ,GAAG,CAAC,EAAE;AAAA,IAC5D;AACA,aAAS;AAAA,EACX;AACF;AAEA,IAAI,QAAQ,SAAS,QAAQ;AAC3B,SAAO,UAAU,CAAC,YAAuD,IAAI,WAAW,OAAO;AACjG,OAAO;AACL,GAAC,MAAM,IAAI,WAAW,GAAG;AAC3B;",
6
+ "names": []
7
+ }