homebridge-plugin-utils 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,366 @@
1
+ export class FeatureOptions {
2
+ _categories;
3
+ _configuredOptions;
4
+ _groups;
5
+ _options;
6
+ defaultReturnValue;
7
+ defaults;
8
+ valueOptions;
9
+ // Create a feature option instance.
10
+ constructor(categories, options, configuredOptions) {
11
+ // Initialize our defaults.
12
+ this._categories = [];
13
+ this._configuredOptions = [];
14
+ this._groups = {};
15
+ this._options = {};
16
+ this.defaultReturnValue = false;
17
+ this.defaults = {};
18
+ this.valueOptions = {};
19
+ this.categories = categories ?? [];
20
+ this.configuredOptions = configuredOptions;
21
+ this.options = options ?? {};
22
+ }
23
+ color(option, device) {
24
+ switch (this.scope(option, device)) {
25
+ case "device":
26
+ return "text-info";
27
+ case "controller":
28
+ return "text-success";
29
+ case "global":
30
+ return device ? "text-warning" : "text-info";
31
+ default:
32
+ return "";
33
+ }
34
+ }
35
+ /**
36
+ * Return the default value for an option.
37
+ *
38
+ * @param option - Feature option to check.
39
+ *
40
+ * @returns Returns true or false, depending on the option default.
41
+ */
42
+ defaultValue(option) {
43
+ // Value-centric feature options don't have default values.
44
+ if (this.isValue(option)) {
45
+ return this.defaultReturnValue;
46
+ }
47
+ const value = this.defaults[option.toLowerCase()];
48
+ // If it's unknown to us, assume it's true.
49
+ if (value === undefined) {
50
+ return this.defaultReturnValue;
51
+ }
52
+ return value;
53
+ }
54
+ /**
55
+ * Return whether the option explicitly exists in the list of configured options.
56
+ *
57
+ * @param option - Feature option to check.
58
+ * @param id - Optional device or controller scope identifier to check.
59
+ *
60
+ * @returns Returns true if the option has been explicitly configured, false otherwise.
61
+ */
62
+ exists(option, id) {
63
+ const regex = this.isValue(option) ? this.valueRegex(option, id) : this.optionRegex(option, id);
64
+ return this.configuredOptions.some(x => regex.test(x));
65
+ }
66
+ /**
67
+ * Return a fully formed feature option string.
68
+ *
69
+ * @param category - Feature option category entry or category name string.
70
+ * @param option - Feature option entry of option name string.
71
+ *
72
+ * @returns Returns a fully formed feature option in the form of `category.option`.
73
+ */
74
+ expandOption(category, option) {
75
+ const categoryName = (typeof category === "string") ? category : category.name;
76
+ const optionName = (typeof option === "string") ? option : option.name;
77
+ if (!categoryName || !categoryName.length) {
78
+ return "";
79
+ }
80
+ return (!optionName || !optionName.length) ? categoryName : categoryName + "." + optionName;
81
+ }
82
+ /**
83
+ * Parse a floating point feature option value.
84
+ *
85
+ * @param value - Value to parse.
86
+ *
87
+ * @returns Returns a floating point number from a string, or `undefined` if it couldn't be parsed.
88
+ */
89
+ getFloat(value) {
90
+ // We don't have the value configured -- we're done.
91
+ if (value === undefined) {
92
+ return undefined;
93
+ }
94
+ // Parse the number and return the value.
95
+ return this.parseOptionNumeric(value, parseFloat);
96
+ }
97
+ /**
98
+ * Parse an integer feature option value.
99
+ *
100
+ * @param value - Value to parse.
101
+ *
102
+ * @returns Returns an integer from a string, or `undefined` if it couldn't be parsed.
103
+ */
104
+ getInteger(value) {
105
+ // We don't have the value configured -- we're done.
106
+ if (value === undefined) {
107
+ return undefined;
108
+ }
109
+ // Parse the number and return the value.
110
+ return this.parseOptionNumeric(value, parseInt);
111
+ }
112
+ /**
113
+ * Return whether an option has been set in either the device or controller scope context.
114
+ *
115
+ * @param option - Feature option to check.
116
+ *
117
+ * @returns Returns true if the option is set at the device or controller level and false otherwise.
118
+ */
119
+ isScopeDevice(option, device) {
120
+ const value = this.exists(option, device);
121
+ // Return the value if it's set, or the default value for this option.
122
+ return (value !== undefined) ? value : this.defaultValue(option);
123
+ }
124
+ /**
125
+ * Return whether an option has been set in the global scope context.
126
+ *
127
+ * @param option - Feature option to check.
128
+ *
129
+ * @returns Returns true if the option is set globally and false otherwise.
130
+ */
131
+ isScopeGlobal(option) {
132
+ const value = this.exists(option);
133
+ // Return the value if it's set, or the default value for this option.
134
+ return (value !== undefined) ? value : this.defaultValue(option);
135
+ }
136
+ /**
137
+ * Return whether an option is value-centric or not.
138
+ *
139
+ * @param option - Feature option entry or string to check.
140
+ *
141
+ * @returns Returns true if it is a value-centric option and false otherwise.
142
+ */
143
+ isValue(option) {
144
+ return this.valueOptions[option?.toLowerCase()] === true;
145
+ }
146
+ /**
147
+ * Return the scope hierarchy location of an option.
148
+ *
149
+ * @param option - Feature option to check.
150
+ * @param device - Optional device scope identifier.
151
+ * @param controller - Optional controller scope identifier.
152
+ *
153
+ * @returns Returns an object containing the location in the scope hierarchy of an `option` as well as the current value associated with the option.
154
+ */
155
+ scope(option, device, controller) {
156
+ return this.getOptionInfo(option, device, controller).scope;
157
+ }
158
+ /**
159
+ * Return the current state of a feature option, traversing the scope hierarchy.
160
+ *
161
+ * @param option - Feature option to check.
162
+ * @param device - Optional device scope identifier.
163
+ * @param controller - Optional controller scope identifier.
164
+ *
165
+ * @returns Returns true if the option is enabled, and false otherwise.
166
+ */
167
+ test(option, device, controller) {
168
+ return this.getOptionInfo(option, device, controller).value;
169
+ }
170
+ /**
171
+ * Return the value associated with a value-centric feature option, traversing the scope hierarchy.
172
+ *
173
+ * @param option - Feature option to check.
174
+ * @param device - Optional device scope identifier.
175
+ * @param controller - Optional controller scope identifier.
176
+ *
177
+ * @returns Returns the current value associated with `option` or `undefined` if none.
178
+ */
179
+ value(option, device, controller) {
180
+ const getValue = (checkOption, checkId) => {
181
+ const regex = this.valueRegex(checkOption, checkId);
182
+ // Get the option value, if we have one.
183
+ for (const entry of this.configuredOptions) {
184
+ const regexMatch = regex.exec(entry);
185
+ if (regexMatch) {
186
+ return regexMatch[1];
187
+ }
188
+ }
189
+ return undefined;
190
+ };
191
+ // Check to see if we have a device-level value first.
192
+ if (device) {
193
+ const value = getValue(option, device);
194
+ if (value) {
195
+ return value;
196
+ }
197
+ }
198
+ // Now check to see if we have an controller-level value.
199
+ if (controller) {
200
+ const value = getValue(option, controller);
201
+ if (value) {
202
+ return value;
203
+ }
204
+ }
205
+ // Finally, we check for a global-level value.
206
+ return getValue(option);
207
+ }
208
+ /**
209
+ * Return the list of available feature option categories.
210
+ *
211
+ * @returns Returns the current list of available feature option categories.
212
+ */
213
+ get categories() {
214
+ return this._categories;
215
+ }
216
+ /**
217
+ * Set the list of available feature option categories.
218
+ *
219
+ * @param options - Array of available feature options.
220
+ */
221
+ set categories(category) {
222
+ this._categories = category;
223
+ }
224
+ /**
225
+ * Return the list of currently configured feature options.
226
+ *
227
+ * @returns Returns the currently configured list of feature options.
228
+ */
229
+ get configuredOptions() {
230
+ return this._configuredOptions;
231
+ }
232
+ /**
233
+ * Set the list of currently configured feature options.
234
+ *
235
+ * @param options - Array of configured feature options.
236
+ */
237
+ set configuredOptions(options) {
238
+ this._configuredOptions = options ?? [];
239
+ }
240
+ /**
241
+ * Return the list of available feature option groups.
242
+ *
243
+ * @returns Returns the current list of available feature option groups.
244
+ */
245
+ get groups() {
246
+ return this._groups;
247
+ }
248
+ /**
249
+ * Return the list of available feature options.
250
+ *
251
+ * @returns Returns the current list of available feature options.
252
+ */
253
+ get options() {
254
+ return this._options;
255
+ }
256
+ /**
257
+ * Set the list of available feature options.
258
+ *
259
+ * @param options - Array of available feature options.
260
+ */
261
+ set options(options) {
262
+ this._options = options ?? {};
263
+ // Regenerate our defaults.
264
+ this.generateDefaults();
265
+ }
266
+ // Build our list of default values for our feature options.
267
+ generateDefaults() {
268
+ this.defaults = {};
269
+ this._groups = {};
270
+ this.valueOptions = {};
271
+ for (const category of this.categories) {
272
+ // Now enumerate all the feature options for a given device and add then to the full list.
273
+ for (const option of this.options[category.name]) {
274
+ // Expand the entry.
275
+ const entry = this.expandOption(category, option);
276
+ // Index the default value.
277
+ this.defaults[entry.toLowerCase()] = option.default;
278
+ // Track value-centric options.
279
+ this.valueOptions[entry.toLowerCase()] = "defaultValue" in option;
280
+ // Cross reference the feature option group it belongs to, if any.
281
+ if (option.group !== undefined) {
282
+ const expandedGroup = category.name + (option.group.length ? ("." + option.group) : "");
283
+ // Initialize the group entry if needed and add the entry.
284
+ (this._groups[expandedGroup] ??= []).push(entry);
285
+ }
286
+ }
287
+ }
288
+ }
289
+ // Utility function to return the setting of a particular option and it's position in the scoping hierarchy.
290
+ getOptionInfo(option, device, controller) {
291
+ // There are a couple of ways to enable and disable options. The rules of the road are:
292
+ //
293
+ // 1. Explicitly disabling, or enabling an option on the controller propogates to all the devices that are managed by that controller. Why might you want to do this?
294
+ // Because...
295
+ //
296
+ // 2. Explicitly disabling, or enabling an option on a device always override the above. This means that it's possible to disable an option for a controller, and all
297
+ // the devices that are managed by it, and then override that behavior on a single device that it's managing.
298
+ // Check to see if we have a device-level option first.
299
+ if (device && this.exists(option, device)) {
300
+ const value = this.isOptionEnabled(option, device);
301
+ if (value !== undefined) {
302
+ return { scope: "device", value: value };
303
+ }
304
+ }
305
+ // Now check to see if we have an controller-level option.
306
+ if (controller && this.exists(option, controller)) {
307
+ const value = this.isOptionEnabled(option, controller);
308
+ if (value !== undefined) {
309
+ return { scope: "controller", value: value };
310
+ }
311
+ }
312
+ // Finally, we check for a global-level value.
313
+ if (this.exists(option)) {
314
+ const value = this.isOptionEnabled(option);
315
+ if (value !== undefined) {
316
+ return { scope: "global", value: value };
317
+ }
318
+ }
319
+ // The option hasn't been set at any scope, return our default value.
320
+ return { scope: "none", value: this.defaultValue(option) };
321
+ }
322
+ // Utility to test whether an option is set in a given scope.
323
+ // We return true if an option is enabled, false for disabled, undefined otherwise. For value-centric options, we return true if a value exists.
324
+ isOptionEnabled(option, id) {
325
+ // Deal with value-centric options uniquely.
326
+ if (this.isValue(option)) {
327
+ return this.exists(option, id);
328
+ }
329
+ const regex = this.optionRegex(option, id);
330
+ // Get the option value, if we have one.
331
+ for (const entry of this.configuredOptions) {
332
+ const regexMatch = regex.exec(entry);
333
+ if (regexMatch) {
334
+ return regexMatch[1].toLowerCase() === "enable";
335
+ }
336
+ }
337
+ return undefined;
338
+ }
339
+ // Regular expression test for feature options.
340
+ optionRegex(option, id) {
341
+ // This regular expression is a bit more intricate than you might think it should be due to the need to ensure we capture values at the very end of the option. We
342
+ // also need to escape out our option to ensure we have no inadvertent issues in matching the regular expression.
343
+ return new RegExp("^(Enable|Disable)\\." + option.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + (!id ? "" : "\\." + id) + "$", "gi");
344
+ }
345
+ // Utility function to parse and return a numeric configuration parameter.
346
+ parseOptionNumeric(option, convert) {
347
+ // We don't have the option configured -- we're done.
348
+ if (option === undefined) {
349
+ return undefined;
350
+ }
351
+ // Convert it to a number, if needed.
352
+ const convertedValue = convert(option);
353
+ // Let's validate to make sure it's really a number.
354
+ if (isNaN(convertedValue) || (convertedValue < 0)) {
355
+ return undefined;
356
+ }
357
+ // Return the value.
358
+ return convertedValue;
359
+ }
360
+ // Regular expression test for value-centric feature options.
361
+ valueRegex(option, id) {
362
+ // This regular expression is a bit more intricate than you might think it should be due to the need to ensure we capture values at the very end of the option.
363
+ return new RegExp("^Enable\\." + option.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + (!id ? "" : "\\." + id) + "\\.([^\\.]+)$", "gi");
364
+ }
365
+ }
366
+ //# sourceMappingURL=featureoptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"featureoptions.js","sourceRoot":"","sources":["../src/featureoptions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export * from "./featureoptions.js";
2
+ export * from "./mqttclient.js";
3
+ export * from "./utils.js";
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ /* Copyright(C) 2017-2024, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * index.ts: Homebridge plugin utilities.
4
+ */
5
+ export * from "./featureoptions.js";
6
+ export * from "./mqttclient.js";
7
+ export * from "./utils.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC"}
@@ -0,0 +1,19 @@
1
+ /// <reference types="node" />
2
+ import { HomebridgePluginLogging } from "./utils.js";
3
+ export declare class MqttClient {
4
+ private brokerUrl;
5
+ private isConnected;
6
+ private reconnectInterval;
7
+ private log;
8
+ private mqtt;
9
+ private subscriptions;
10
+ private topicPrefix;
11
+ constructor(brokerUrl: string, topicPrefix: string, log: HomebridgePluginLogging, reconnectInterval?: number);
12
+ private configure;
13
+ publish(id: string, topic: string, message: string): void;
14
+ subscribe(id: string, topic: string, callback: (cbBuffer: Buffer) => void): void;
15
+ subscribeGet(id: string, topic: string, type: string, getValue: () => string, log?: HomebridgePluginLogging): void;
16
+ subscribeSet(id: string, topic: string, type: string, setValue: (value: string, rawValue: string) => Promise<void> | void, log?: HomebridgePluginLogging): void;
17
+ unsubscribe(id: string, topic: string): void;
18
+ private expandTopic;
19
+ }
@@ -0,0 +1,163 @@
1
+ import mqtt from "mqtt";
2
+ import util from "node:util";
3
+ const MQTT_DEFAULT_RECONNECT_INTERVAL = 60;
4
+ export class MqttClient {
5
+ brokerUrl;
6
+ isConnected;
7
+ reconnectInterval;
8
+ log;
9
+ mqtt;
10
+ subscriptions;
11
+ topicPrefix;
12
+ constructor(brokerUrl, topicPrefix, log, reconnectInterval = MQTT_DEFAULT_RECONNECT_INTERVAL) {
13
+ this.brokerUrl = brokerUrl;
14
+ this.isConnected = false;
15
+ this.log = log;
16
+ this.mqtt = null;
17
+ this.reconnectInterval = reconnectInterval;
18
+ this.subscriptions = {};
19
+ this.topicPrefix = topicPrefix;
20
+ this.configure();
21
+ }
22
+ // Connect to the MQTT broker.
23
+ configure() {
24
+ // Try to connect to the MQTT broker and make sure we catch any URL errors.
25
+ try {
26
+ this.mqtt = mqtt.connect(this.brokerUrl, { reconnectPeriod: this.reconnectInterval * 1000, rejectUnauthorized: false });
27
+ }
28
+ catch (error) {
29
+ if (error instanceof Error) {
30
+ switch (error.message) {
31
+ case "Missing protocol":
32
+ this.log.error("MQTT Broker: Invalid URL provided: %s.", this.brokerUrl);
33
+ break;
34
+ default:
35
+ this.log.error("MQTT Broker: Error: %s.", error.message);
36
+ break;
37
+ }
38
+ }
39
+ }
40
+ // We've been unable to even attempt to connect. It's likely we have a configuration issue - we're done here.
41
+ if (!this.mqtt) {
42
+ return;
43
+ }
44
+ // Notify the user when we connect to the broker.
45
+ this.mqtt.on("connect", () => {
46
+ this.isConnected = true;
47
+ // Inform users, while redacting authentication credentials.
48
+ this.log.info("MQTT Broker: Connected to %s (topic: %s).", this.brokerUrl.replace(/^(.*:\/\/.*:)(.*)(@.*)$/, "$1REDACTED$3"), this.topicPrefix);
49
+ });
50
+ // Notify the user when we've disconnected.
51
+ this.mqtt.on("close", () => {
52
+ // We only inform users if we're already connected. Otherwise, we're likely in an error state and that's logged elsewhere.
53
+ if (!this.isConnected) {
54
+ return;
55
+ }
56
+ this.isConnected = false;
57
+ // Inform users.
58
+ this.log.info("MQTT Broker: Connection closed.");
59
+ });
60
+ // Process inbound messages and pass it to the right message handler.
61
+ this.mqtt.on("message", (topic, message) => {
62
+ this.subscriptions[topic]?.(message);
63
+ });
64
+ // Notify the user when there's a connectivity error.
65
+ this.mqtt.on("error", (error) => {
66
+ const logError = (message) => this.log.error("MQTT Broker: %s. Will retry again in %s minute%s.", message, this.reconnectInterval / 60, this.reconnectInterval / 60 > 1 ? "s" : "");
67
+ switch (error.code) {
68
+ case "ECONNREFUSED":
69
+ logError("Connection refused");
70
+ break;
71
+ case "ECONNRESET":
72
+ logError("Connection reset");
73
+ break;
74
+ case "ENOTFOUND":
75
+ this.mqtt?.end(true);
76
+ this.log.error("MQTT Broker: Hostname or IP address not found.");
77
+ break;
78
+ default:
79
+ logError(util.inspect(error, { sorted: true }));
80
+ break;
81
+ }
82
+ });
83
+ }
84
+ // Publish an MQTT event to a broker.
85
+ publish(id, topic, message) {
86
+ const expandedTopic = this.expandTopic(id, topic);
87
+ // No valid topic returned, we're done.
88
+ if (!expandedTopic) {
89
+ return;
90
+ }
91
+ this.log.debug("MQTT publish: %s Message: %s.", expandedTopic, message);
92
+ // By default, we publish as: pluginTopicPrefix/id/topic
93
+ this.mqtt?.publish(expandedTopic, message);
94
+ }
95
+ // Subscribe to an MQTT topic.
96
+ subscribe(id, topic, callback) {
97
+ const expandedTopic = this.expandTopic(id, topic);
98
+ // No valid topic returned, we're done.
99
+ if (!expandedTopic) {
100
+ return;
101
+ }
102
+ this.log.debug("MQTT subscribe: %s.", expandedTopic);
103
+ // Add to our callback list.
104
+ this.subscriptions[expandedTopic] = callback;
105
+ // Tell MQTT we're subscribing to this event.
106
+ // By default, we subscribe as: pluginTopicPrefix/id/topic
107
+ this.mqtt?.subscribe(expandedTopic);
108
+ }
109
+ // Subscribe to a specific MQTT topic and publish a value on a get request.
110
+ subscribeGet(id, topic, type, getValue, log = this.log) {
111
+ // Return the current status of a given sensor.
112
+ this.subscribe(id, topic + "/get", (message) => {
113
+ const value = message.toString().toLowerCase();
114
+ // When we get the right message, we return the system information JSON.
115
+ if (value !== "true") {
116
+ return;
117
+ }
118
+ this.publish(id, topic, getValue());
119
+ log.info("MQTT: %s status published.", type);
120
+ });
121
+ }
122
+ // Subscribe to a specific MQTT topic and set a value on a set request.
123
+ subscribeSet(id, topic, type, setValue, log = this.log) {
124
+ // Return the current status of a given sensor.
125
+ this.subscribe(id, topic + "/set", (message) => {
126
+ const value = message.toString().toLowerCase();
127
+ const logResult = () => {
128
+ log.info("MQTT: set message received for %s: %s.", type, value);
129
+ };
130
+ // Set our value and inform the user.
131
+ const result = setValue(value, message.toString());
132
+ // For callbacks that are promises, we wait until they complete before logging the result.
133
+ if (result && typeof result.then === "function") {
134
+ result.then(() => {
135
+ logResult();
136
+ }).catch(error => {
137
+ log.error("MQTT: error seting message received for %s: %s. %s", type, value, error);
138
+ });
139
+ return;
140
+ }
141
+ // Log the outcome.
142
+ logResult();
143
+ });
144
+ }
145
+ // Unsubscribe to an MQTT topic.
146
+ unsubscribe(id, topic) {
147
+ const expandedTopic = this.expandTopic(id, topic);
148
+ // No valid topic returned, we're done.
149
+ if (!expandedTopic) {
150
+ return;
151
+ }
152
+ delete this.subscriptions[expandedTopic];
153
+ }
154
+ // Expand a topic to a unique, fully formed one.
155
+ expandTopic(id, topic) {
156
+ // No id, we're done.
157
+ if (!id) {
158
+ return null;
159
+ }
160
+ return this.topicPrefix + "/" + id + "/" + topic;
161
+ }
162
+ }
163
+ //# sourceMappingURL=mqttclient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mqttclient.js","sourceRoot":"","sources":["../src/mqttclient.ts"],"names":[],"mappings":"AAKA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,+BAA+B,GAAG,EAAE,CAAC;AAE3C,MAAM,OAAO,UAAU;IAEb,SAAS,CAAS;IAClB,WAAW,CAAU;IACrB,iBAAiB,CAAS;IAC1B,GAAG,CAA0B;IAC7B,IAAI,CAAyB;IAC7B,aAAa,CAAkD;IAC/D,WAAW,CAAS;IAE5B,YAAY,SAAiB,EAAE,WAAmB,EAAE,GAA4B,EAAE,iBAAiB,GAAG,+BAA+B;QAEnI,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;QAC3C,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QACxB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAE/B,IAAI,CAAC,SAAS,EAAE,CAAC;IACnB,CAAC;IAED,8BAA8B;IACtB,SAAS;QAEf,2EAA2E;QAC3E,IAAI,CAAC;YAEH,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,eAAe,EAAE,IAAI,CAAC,iBAAiB,GAAG,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAC,CAAC,CAAC;QAEzH,CAAC;QAAC,OAAM,KAAK,EAAE,CAAC;YAEd,IAAG,KAAK,YAAY,KAAK,EAAE,CAAC;gBAE1B,QAAO,KAAK,CAAC,OAAO,EAAE,CAAC;oBAErB,KAAK,kBAAkB;wBAErB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,wCAAwC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;wBACzE,MAAM;oBAER;wBAEE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;wBACzD,MAAM;gBACV,CAAC;YACH,CAAC;QACH,CAAC;QAED,6GAA6G;QAC7G,IAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAEd,OAAO;QACT,CAAC;QAED,iDAAiD;QACjD,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;YAE3B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YAExB,4DAA4D;YAC5D,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,2CAA2C,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,yBAAyB,EAAE,cAAc,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QAClJ,CAAC,CAAC,CAAC;QAEH,2CAA2C;QAC3C,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YAEzB,0HAA0H;YAC1H,IAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBAErB,OAAO;YACT,CAAC;YAED,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;YAEzB,gBAAgB;YAChB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;QAEH,qEAAqE;QACrE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,KAAa,EAAE,OAAe,EAAE,EAAE;YAEzD,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;QAEH,qDAAqD;QACrD,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAY,EAAE,EAAE;YAErC,MAAM,QAAQ,GAAG,CAAC,OAAe,EAAQ,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,mDAAmD,EAAE,OAAO,EAAE,IAAI,CAAC,iBAAiB,GAAG,EAAE,EAClJ,IAAI,CAAC,iBAAiB,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAE9C,QAAQ,KAA+B,CAAC,IAAI,EAAE,CAAC;gBAE7C,KAAK,cAAc;oBAEjB,QAAQ,CAAC,oBAAoB,CAAC,CAAC;oBAE/B,MAAM;gBAER,KAAK,YAAY;oBAEf,QAAQ,CAAC,kBAAkB,CAAC,CAAC;oBAE7B,MAAM;gBAER,KAAK,WAAW;oBAEd,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;oBACrB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;oBAEjE,MAAM;gBAER;oBAEE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;oBAEhD,MAAM;YACV,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,qCAAqC;IAC9B,OAAO,CAAC,EAAU,EAAE,KAAa,EAAE,OAAe;QAEvD,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAElD,uCAAuC;QACvC,IAAG,CAAC,aAAa,EAAE,CAAC;YAElB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,+BAA+B,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;QAExE,wDAAwD;QACxD,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,8BAA8B;IACvB,SAAS,CAAC,EAAU,EAAE,KAAa,EAAE,QAAoC;QAE9E,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAElD,uCAAuC;QACvC,IAAG,CAAC,aAAa,EAAE,CAAC;YAElB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,qBAAqB,EAAE,aAAa,CAAC,CAAC;QAErD,4BAA4B;QAC5B,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,GAAG,QAAQ,CAAC;QAE7C,6CAA6C;QAC7C,0DAA0D;QAC1D,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;IACtC,CAAC;IAED,2EAA2E;IACpE,YAAY,CAAC,EAAU,EAAE,KAAa,EAAE,IAAY,EAAE,QAAsB,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG;QAEjG,+CAA+C;QAC/C,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,KAAK,GAAG,MAAM,EAAE,CAAC,OAAe,EAAE,EAAE;YAErD,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE,CAAC;YAE/C,wEAAwE;YACxE,IAAG,KAAK,KAAK,MAAM,EAAE,CAAC;gBAEpB,OAAO;YACT,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YACpC,GAAG,CAAC,IAAI,CAAC,4BAA4B,EAAE,IAAI,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;IACL,CAAC;IAED,uEAAuE;IAChE,YAAY,CAAC,EAAU,EAAE,KAAa,EAAE,IAAY,EAAE,QAAmE,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG;QAE9I,+CAA+C;QAC/C,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,KAAK,GAAG,MAAM,EAAE,CAAC,OAAe,EAAE,EAAE;YAErD,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE,CAAC;YAE/C,MAAM,SAAS,GAAG,GAAS,EAAE;gBAE3B,GAAG,CAAC,IAAI,CAAC,wCAAwC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;YAClE,CAAC,CAAC;YAEF,qCAAqC;YACrC,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;YAEnD,0FAA0F;YAC1F,IAAG,MAAM,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAE/C,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;oBAEf,SAAS,EAAE,CAAC;gBACd,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;oBAEf,GAAG,CAAC,KAAK,CAAC,oDAAoD,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;gBACtF,CAAC,CAAC,CAAC;gBAEH,OAAO;YACT,CAAC;YAED,mBAAmB;YACnB,SAAS,EAAE,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;IAED,gCAAgC;IACzB,WAAW,CAAC,EAAU,EAAE,KAAa;QAE1C,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAElD,uCAAuC;QACvC,IAAG,CAAC,aAAa,EAAE,CAAC;YAElB,OAAO;QACT,CAAC;QAED,OAAO,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;IAC3C,CAAC;IAED,gDAAgD;IACxC,WAAW,CAAC,EAAU,EAAE,KAAa;QAE3C,qBAAqB;QACrB,IAAG,CAAC,EAAE,EAAE,CAAC;YAEP,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,IAAI,CAAC,WAAW,GAAG,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG,KAAK,CAAC;IACnD,CAAC;CACF"}