UNPKG

@iotize/ionic

Version:

Iotize specific building blocks on top of @ionic/angular.

284 lines 36.5 kB
import { Injectable } from '@angular/core'; import { Platform } from '@ionic/angular'; import { HostProtocol } from '@iotize/tap'; import { WifiMode } from '@iotize/tap/service/impl/wifi'; import { ReplaySubject } from 'rxjs'; import { CONFIGURATION_MODE_VERSION, FACTORY_RESET_MODE_VERSION, } from '../../config-version'; import { CurrentDeviceService } from '../../current-device.service'; import { debug } from '../../logger'; import { TapInfoRequestService } from '../../tap-info/tap-info-request.service'; import { TapInfo } from '../../tap-info/keys'; import * as i0 from "@angular/core"; import * as i1 from "../../current-device.service"; import * as i2 from "../../tap-info/tap-info-request.service"; import * as i3 from "@ionic/angular"; const MQTT_SECURE_PORTS = ['8883']; const TAG = 'TapConnectionStoreService'; export function filterByConnectionTypes(filteredConnectionTypes) { return (values) => { if (!filteredConnectionTypes) { return values; } return values.filter((value) => filteredConnectionTypes.indexOf(value.connection.type) >= 0); }; } /** * TODO change implementation to not trigger NFC load * Use reactive programming instead */ export class TapConnectionStoreService { tapService; infoResolver; platform; items = []; values = new ReplaySubject(1); config = { localStorageKey: 'TapConnectionStoreService.items', mqtt: { webSocket: { port: 9001, // TODO configurable }, username: undefined, password: undefined, }, }; constructor(tapService, infoResolver, platform) { this.tapService = tapService; this.infoResolver = infoResolver; this.platform = platform; this.loadFromStorage(); } set(items) { if (!items) { items = []; } this.items = items; this.values.next(this.items); this.saveToStorage(); } remove(item) { this.items.splice(this.items.indexOf(item), 1); this.set(this.items); } /** * Try to add or refresh current stored connection info * */ async storeCurrentTapConnectionInfo() { const connection = this.tapService.protocolMeta; if (!connection) { debug(`${TAG} No protocol connection info for this Tap. Cannot store connection info.`); return false; } const [serialNumber, version, appName, availableHostProtocols, currentHostProtocol,] = await this.infoResolver.getValues([ TapInfo.serialNumber, TapInfo.configVersion, TapInfo.appName, TapInfo.availableHostProtocols, TapInfo.HostProtocol, ]); if (version === FACTORY_RESET_MODE_VERSION || version === CONFIGURATION_MODE_VERSION) { return false; } const config = { serialNumber, version, appName, }; this.addOrRefresh({ connection, config, }); const additionalProtocols = await this._resolveConnectionProtocols(availableHostProtocols, currentHostProtocol); additionalProtocols.forEach((connection) => { this.addOrRefresh({ connection, config, }); }); return true; } _createMqttProtocolFromTapInfo({ brokerHostname, clientId, password, username, netkey, port, serialNumber, }) { clientId = clientId || `${serialNumber}_${Math.round(Math.random() * 1000000)}`; username = this.config.mqtt.username || username; password = this.config.mqtt.password || password; let brokerPort; if (this.platform.is('mobileweb')) { if (!this.config.mqtt.webSocket?.port) { debug(TAG, 'MQTT over web socket is not available (no port configured. Skipping...)'); return undefined; } brokerPort = this.config.mqtt.webSocket.port.toString(); } else { brokerPort = port; } let url; if (!brokerHostname.match(/^[a-z]+:\/\//i)) { let mqttProtocol = this.platform.is('mobileweb') ? 'ws' : 'mqtt'; if (MQTT_SECURE_PORTS.includes(port)) { mqttProtocol += 's'; } url = `${mqttProtocol}://${brokerHostname}`; } else { url = brokerHostname; if (this.platform.is('mobileweb')) { url = url.replace(/^(mqtt)s?:\/\//iu, 'ws'); } } return { type: 'mqtt', info: { username, password, serialNumber, netkey, clientId, endpoint: `${url}:${brokerPort}`, }, }; } async _resolveConnectionProtocols(availableHostProtocols, currentHostProtocol) { debug(TAG, `availableHostProtocols ${availableHostProtocols .map((protocol) => HostProtocol[protocol]) .join(', ')}...`); const results = []; for (const protocol of availableHostProtocols) { if (protocol === HostProtocol.NFC || protocol === currentHostProtocol) { continue; } try { const connectionInfo = await this._resolveConnectionProtocol(protocol); if (connectionInfo) { results.push(connectionInfo); } } catch (err) { console.warn(`Cannot resolve protocol ${HostProtocol[protocol]}`); // TODO log ? } } return results; } async _resolveConnectionProtocol(protocol) { debug(TAG, `_resolveConnectionProtocol ${HostProtocol[protocol]}...`); switch (protocol) { case HostProtocol.BLE: return { type: 'ble', info: { mac: await this.infoResolver.getValue(TapInfo.bleMacAddress), }, }; case HostProtocol.MQTT: let [brokerHostname, serialNumber, netkey, mqttUsername, mqttPassword, port, clientId,] = await this.infoResolver.getValues([ TapInfo.MqttBrokerHostname, TapInfo.serialNumber, TapInfo.MqttRelayNetKey, TapInfo.MqttUsername, TapInfo.MqttPassword, TapInfo.MqttBrokerPort, TapInfo.MqttClientId, ]); return this._createMqttProtocolFromTapInfo({ brokerHostname, serialNumber, netkey, username: mqttUsername, password: mqttPassword, port, clientId, }); case HostProtocol.WIFI: const [wifiMode, wifiHostname, wifiSSID, wifiKey] = await this.infoResolver.getValues([ TapInfo.WifiMode, TapInfo.WifiHostname, TapInfo.WifiSSID, TapInfo.WifiKey, ]); if (wifiHostname && wifiHostname !== '0.0.0.0') { debug(TAG, `resolveConnectionProtocol wifi mode ${WifiMode[wifiMode]}...`); switch (wifiMode) { case WifiMode.NETWORK: return { type: 'socket', info: { url: `tcp://${wifiHostname}:2000`, }, }; case WifiMode.PEER_TO_PEER: return { type: 'wifi', info: { ssid: wifiSSID, url: `tcp://${wifiHostname}:2000`, password: wifiKey, }, }; default: return undefined; } } default: debug(TAG, `resolveConnectionProtocol nothing to store for protocol ${HostProtocol[protocol]}`); return undefined; } } addOrRefresh(newItem) { debug(`${TAG} Adding/updating new stored device ${JSON.stringify(newItem)}...`); const existingItem = this.items.find((item) => { return (item.config.serialNumber === newItem.config.serialNumber && item.connection.type === newItem.connection.type); }); if (existingItem) { existingItem.config = newItem.config; existingItem.connection = newItem.connection; existingItem.meta.lastConnection = new Date(); } else { this.items.push({ connection: newItem.connection, config: newItem.config, meta: { state: 'online', lastConnection: new Date(), }, }); } debug(`${TAG} Added/updated new stored device ${newItem.config.serialNumber} ${newItem.connection.type}`); this.set(this.items); } clear() { this.set([]); } saveToStorage() { localStorage.setItem(this.config.localStorageKey, JSON.stringify(this.items)); } loadFromStorage() { try { const content = localStorage.getItem(this.config.localStorageKey); if (content) { this.set(JSON.parse(content)); } else { this.set([]); } } catch (err) { console.warn('Cannot load stored connection from local storage', err); this.set([]); } } /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TapConnectionStoreService, deps: [{ token: i1.CurrentDeviceService }, { token: i2.TapInfoRequestService }, { token: i3.Platform }], target: i0.ɵɵFactoryTarget.Injectable }); /** @nocollapse */ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TapConnectionStoreService, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TapConnectionStoreService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }], ctorParameters: () => [{ type: i1.CurrentDeviceService }, { type: i2.TapInfoRequestService }, { type: i3.Platform }] }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFwLWNvbm5lY3Rpb24tc3RvcmUuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2lvdGl6ZS1pb25pYy9zcmMvbGliL3RhcC1jb25uZWN0L3RhcC1jb25uZWN0aW9uLXN0b3JlL3RhcC1jb25uZWN0aW9uLXN0b3JlLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMzQyxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDMUMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUMzQyxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDekQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUVyQyxPQUFPLEVBQ0wsMEJBQTBCLEVBQzFCLDBCQUEwQixHQUMzQixNQUFNLHNCQUFzQixDQUFDO0FBQzlCLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBRXBFLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDckMsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0seUNBQXlDLENBQUM7QUFDaEYsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLHFCQUFxQixDQUFDOzs7OztBQUc5QyxNQUFNLGlCQUFpQixHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7QUFFbkMsTUFBTSxHQUFHLEdBQUcsMkJBQTJCLENBQUM7QUFFeEMsTUFBTSxVQUFVLHVCQUF1QixDQUNyQyx1QkFBNkM7SUFFN0MsT0FBTyxDQUFDLE1BQWdDLEVBQTRCLEVBQUU7UUFDcEUsSUFBSSxDQUFDLHVCQUF1QixFQUFFLENBQUM7WUFDN0IsT0FBTyxNQUFNLENBQUM7UUFDaEIsQ0FBQztRQUNELE9BQU8sTUFBTSxDQUFDLE1B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QUMsWUFBWSxJQUFJLE9BQU8sQ0FBQyxVQUFVLENBQUMsSUFBSSxFQUFFLENBQ25HLENBQUM7UUFDRixJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUN2QixDQUFDO0lBRUQsS0FBSztRQUNILElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDZixDQUFDO0lBRUQsYUFBYTtRQUNYLFlBQVksQ0FBQyxPQUFPLENBQ2xCLElBQUksQ0FBQyxNQUFNLENBQUMsZUFBZSxFQUMzQixJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FDM0IsQ0FBQztJQUNKLENBQUM7SUFFRCxlQUFlO1FBQ2IsSUFBSSxDQUFDO1lBQ0gsTUFBTSxPQUFPLEdBQUcsWUFBWSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLGVBQWUsQ0FBQyxDQUFDO1lBQ2xFLElBQUksT0FBTyxFQUFFLENBQUM7Z0JBQ1osSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7WUFDaEMsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDZixDQUFDO1FBQ0gsQ0FBQztRQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7WUFDYixPQUFPLENBQUMsSUFBSSxDQUFDLGtEQUFrRCxFQUFFLEdBQUcsQ0FBQyxDQUFDO1lBQ3RFLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDZixDQUFDO0lBQ0gsQ0FBQzsySEF6VVUseUJBQXlCOytIQUF6Qix5QkFBeUIsY0FGeEIsTUFBTTs7NEZBRVAseUJBQXlCO2tCQUhyQyxVQUFVO21CQUFDO29CQUNWLFVBQVUsRUFBRSxNQUFNO2lCQUNuQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IFBsYXRmb3JtIH0gZnJvbSAnQGlvbmljL2FuZ3VsYXInO1xuaW1wb3J0IHsgSG9zdFByb3RvY29sIH0gZnJvbSAnQGlvdGl6ZS90YXAnO1xuaW1wb3J0IHsgV2lmaU1vZGUgfSBmcm9tICdAaW90aXplL3RhcC9zZXJ2aWNlL2ltcGwvd2lmaSc7XG5pbXBvcnQgeyBSZXBsYXlTdWJqZWN0IH0gZnJvbSAncnhqcyc7XG5cbmltcG9ydCB7XG4gIENPTkZJR1VSQVRJT05fTU9ERV9WRVJTSU9OLFxuICBGQUNUT1JZX1JFU0VUX01PREVfVkVSU0lPTixcbn0gZnJvbSAnLi4vLi4vY29uZmlnLXZlcnNpb24nO1xuaW1wb3J0IHsgQ3VycmVudERldmljZVNlcnZpY2UgfSBmcm9tICcuLi8uLi9jdXJyZW50LWRldmljZS5zZXJ2aWNlJztcbmltcG9ydCB7IFByb3RvY29sTWV0YSB9IGZyb20gJy4uLy4uL2V4dGVuc2lvbnMvcHJvdG9jb2wtaW5mbyc7XG5pbXBvcnQgeyBkZWJ1ZyB9IGZyb20gJy4uLy4uL2xvZ2dlcic7XG5pbXBvcnQgeyBUYXBJbmZvUmVxdWVzdFNlcnZpY2UgfSBmcm9tICcuLi8uLi90YXAtaW5mby90YXAtaW5mby1yZXF1ZXN0LnNlcnZpY2UnO1xuaW1wb3J0IHsgVGFwSW5mbyB9IGZyb20gJy4uLy4uL3RhcC1pbmZvL2tleXMnO1xuaW1wb3J0IHsgVGFwQ29ubmVjdGlvblN0b3JlSXRlbSB9IGZyb20gJy4vZGVmaW5pdGlvbnMnO1xuXG5jb25zdCBNUVRUX1NFQ1VSRV9QT1JUUyA9IFsnODg4MyddO1xuXG5jb25zdCBUQUcgPSAnVGFwQ29ubmVjdGlvblN0b3JlU2VydmljZSc7XG5cbmV4cG9ydCBmdW5jdGlvbiBmaWx0ZXJCeUNvbm5lY3Rpb25UeXBlcyhcbiAgZmlsdGVyZWRDb25uZWN0aW9uVHlwZXM6IHN0cmluZ1tdIHwgdW5kZWZpbmVkXG4pIHtcbiAgcmV0dXJuICh2YWx1ZXM6IFRhcENvbm5lY3Rpb25TdG9yZUl0ZW1bXSk6IFRhcENvbm5lY3Rpb25TdG9yZUl0ZW1bXSA9PiB7XG4gICAgaWYgKCFmaWx0ZXJlZENvbm5lY3Rpb25UeXBlcykge1xuICAgICAgcmV0dXJuIHZhbHVlcztcbiAgICB9XG4gICAgcmV0dXJuIHZhbHVlcy5maWx0ZXIoXG4gICAgICAodmFsdWUpID0+IGZpbHRlcmVkQ29ubmVjdGlvblR5cGVzLmluZGV4T2YodmFsdWUuY29ubmVjdGlvbi50eXBlKSA+PSAwXG4gICAgKTtcbiAgfTtcbn1cbmV4cG9ydCBuYW1lc3BhY2UgVGFwQ29ubmVjdGlvblN0b3JlU2VydmljZSB7XG4gIGV4cG9ydCBpbnRlcmZhY2UgSW5zdGFuY2VPcHRpb25zIHtcbiAgICAvKipcbiAgICAgKiBsb2NhbFN0b3JlIGtleSBwYXRoIHRvIHN0b3JlIGNvbm5lY3Rpb25zXG4gICAgICovXG4gICAgbG9jYWxTdG9yYWdlS2V5OiBzdHJpbmc7XG4gICAgLyoqXG4gICAgICogTVFUVCByZWxhdGVkIG9wdGlvbnNcbiAgICAgKi9cbiAgICBtcXR0OiB7XG4gICAgICAvKipcbiAgICAgICAqIEZvciBicm93c2VyIGFwcCwgd2UgbXVzdCBjb25uZWN0IHRvIE1RVFQgYnJva2VyIHRocm91Z2ggd2Vic29ja2V0XG4gICAgICAgKiBXZSBjYW4gc3BlY2lmeSB0aGUgcG9ydCB0byBpbiB0aGlzIGNhc2UuXG4gICAgICAgKiBGb3IgbW9iaWxlIGFwcCB3ZSB1c2UgdGhlIHNhbWUgcG9ydCBjb25maWd1cmVkIGluIHRoZSBUYXBcbiAgICAgICAqL1xuICAgICAgd2ViU29ja2V0Pzoge1xuICAgICAgICBwb3J0OiBudW1iZXI7XG4gICAgICB9O1xuICAgICAgLyoqXG4gICAgICAgKiBNUVRUIGJyb2tlciB1c2VybmFtZSB0byB1c2VyXG4gICAgICAgKiBJZiBub3QgcHJvdmlkZWQgaXQgd2lsbCB1c2UgVEFQIHVzZXJuYW1lXG4gICAgICAgKi9cbiAgICAgIHVzZXJuYW1lPzogc3RyaW5nO1xuICAgICAgLyoqXG4gICAgICAgKiBNUVRUIGJyb2tlciBwYXNzd29yZCB0byB1c2VyXG4gICAgICAgKiBJZiBub3QgcHJvdmlkZWQgaXQgd2lsbCB0cnkgdG8gdXNlIFRBUCBwYXNzd29yZCAoaWYgbm90IHByb3RlY3RlZClcbiAgICAgICAqL1xuICAgICAgcGFzc3dvcmQ/OiBzdHJpbmc7XG4gICAgfTtcbiAgfVxufVxuXG4vKipcbiAqIFRPRE8gY2hhbmdlIGltcGxlbWVudGF0aW9uIHRvIG5vdCB0cmlnZ2VyIE5GQyBsb2FkXG4gKiBVc2UgcmVhY3RpdmUgcHJvZ3JhbW1pbmcgaW5zdGVhZFxuICovXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290Jyxcbn0pXG5leHBvcnQgY2xhc3MgVGFwQ29ubmVjdGlvblN0b3JlU2VydmljZSB7XG4gIGl0ZW1zOiBUYXBDb25uZWN0aW9uU3RvcmVJdGVtW10gPSBbXTtcblxuICB2YWx1ZXMgPSBuZXcgUmVwbGF5U3ViamVjdDxUYXBDb25uZWN0aW9uU3RvcmVJdGVtW10+KDEpO1xuXG4gIGNvbmZpZzogVGFwQ29ubmVjdGlvblN0b3JlU2VydmljZS5JbnN0YW5jZU9wdGlvbnMgPSB7XG4gICAgbG9jYWxTdG9yYWdlS2V5OiAnVGFwQ29ubmVjdGlvblN0b3JlU2VydmljZS5pdGVtcycsXG4gICAgbXF0dDoge1xuICAgICAgd2ViU29ja2V0OiB7XG4gICAgICAgIHBvcnQ6IDkwMDEsIC8vIFRPRE8gY29uZmlndXJhYmxlXG4gICAgICB9LFxuICAgICAgdXNlcm5hbWU6IHVuZGVmaW5lZCxcbiAgICAgIHBhc3N3b3JkOiB1bmRlZmluZWQsXG4gICAgfSxcbiAgfTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHRhcFNlcnZpY2U6IEN1cnJlbnREZXZpY2VTZXJ2aWNlLFxuICAgIHByaXZhdGUgaW5mb1Jlc29sdmVyOiBUYXBJbmZvUmVxdWVzdFNlcnZpY2UsXG4gICAgcHJpdmF0ZSBwbGF0Zm9ybTogUGxhdGZvcm1cbiAgKSB7XG4gICAgdGhpcy5sb2FkRnJvbVN0b3JhZ2UoKTtcbiAgfVxuXG4gIHNldChpdGVtczogVGFwQ29ubmVjdGlvblN0b3JlSXRlbVtdIHwgdW5kZWZpbmVkKSB7XG4gICAgaWYgKCFpdGVtcykge1xuICAgICAgaXRlbXMgPSBbXTtcbiAgICB9XG4gICAgdGhpcy5pdGVtcyA9IGl0ZW1zO1xuICAgIHRoaXMudmFsdWVzLm5leHQodGhpcy5pdGVtcyk7XG4gICAgdGhpcy5zYXZlVG9TdG9yYWdlKCk7XG4gIH1cblxuICByZW1vdmUoaXRlbTogVGFwQ29ubmVjdGlvblN0b3JlSXRlbSkge1xuICAgIHRoaXMuaXRlbXMuc3BsaWNlKHRoaXMuaXRlbXMuaW5kZXhPZihpdGVtKSwgMSk7XG4gICAgdGhpcy5zZXQodGhpcy5pdGVtcyk7XG4gIH1cblxuICAvKipcbiAgICogVHJ5IHRvIGFkZCBvciByZWZyZXNoIGN1cnJlbnQgc3RvcmVkIGNvbm5lY3Rpb24gaW5mb1xuICAgKlxuICAgKi9cbiAgYXN5bmMgc3RvcmVDdXJyZW50VGFwQ29ubmVjdGlvbkluZm8oKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgY29uc3QgY29ubmVjdGlvbiA9IHRoaXMudGFwU2VydmljZS5wcm90b2NvbE1ldGE7XG4gICAgaWYgKCFjb25uZWN0aW9uKSB7XG4gICAgICBkZWJ1ZyhcbiAgICAgICAgYCR7VEFHfSBObyBwcm90b2NvbCBjb25uZWN0aW9uIGluZm8gZm9yIHRoaXMgVGFwLiBDYW5ub3Qgc3RvcmUgY29ubmVjdGlvbiBpbmZvLmBcbiAgICAgICk7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGNvbnN0IFtcbiAgICAgIHNlcmlhbE51bWJlcixcbiAgICAgIHZlcnNpb24sXG4gICAgICBhcHBOYW1lLFxuICAgICAgYXZhaWxhYmxlSG9zdFByb3RvY29scyxcbiAgICAgIGN1cnJlbnRIb3N0UHJvdG9jb2wsXG4gICAgXSA9IGF3YWl0IHRoaXMuaW5mb1Jlc29sdmVyLmdldFZhbHVlczxcbiAgICAgIFtzdHJpbmcsIHN0cmluZywgc3RyaW5nLCBIb3N0UHJvdG9jb2xbXSwgSG9zdFByb3RvY29sXVxuICAgID4oW1xuICAgICAgVGFwSW5mby5zZXJpYWxOdW1iZXIsXG4gICAgICBUYXBJbmZvLmNvbmZpZ1ZlcnNpb24sXG4gICAgICBUYXBJbmZvLmFwcE5hbWUsXG4gICAgICBUYXBJbmZvLmF2YWlsYWJsZUhvc3RQcm90b2NvbHMsXG4gICAgICBUYXBJbmZvLkhvc3RQcm90b2NvbCxcbiAgICBdKTtcblxuICAgIGlmIChcbiAgICAgIHZlcnNpb24gPT09IEZBQ1RPUllfUkVTRVRfTU9ERV9WRVJTSU9OIHx8XG4gICAgICB2ZXJzaW9uID09PSBDT05GSUdVUkFUSU9OX01PREVfVkVSU0lPTlxuICAgICkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIGNvbnN0IGNvbmZpZyA9IHtcbiAgICAgIHNlcmlhbE51bWJlcixcbiAgICAgIHZlcnNpb24sXG4gICAgICBhcHBOYW1lLFxuICAgIH07XG5cbiAgICB0aGlzLmFkZE9yUmVmcmVzaCh7XG4gICAgICBjb25uZWN0aW9uLFxuICAgICAgY29uZmlnLFxuICAgIH0pO1xuXG4gICAgY29uc3QgYWRkaXRpb25hbFByb3RvY29scyA9IGF3YWl0IHRoaXMuX3Jlc29sdmVDb25uZWN0aW9uUHJvdG9jb2xzKFxuICAgICAgYXZhaWxhYmxlSG9zdFByb3RvY29scyxcbiAgICAgIGN1cnJlbnRIb3N0UHJvdG9jb2xcbiAgICApO1xuICAgIGFkZGl0aW9uYWxQcm90b2NvbHMuZm9yRWFjaCgoY29ubmVjdGlvbikgPT4ge1xuICAgICAgdGhpcy5hZGRPclJlZnJlc2goe1xuICAgICAgICBjb25uZWN0aW9uLFxuICAgICAgICBjb25maWcsXG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgcHJpdmF0ZSBfY3JlYXRlTXF0dFByb3RvY29sRnJvbVRhcEluZm8oe1xuICAgIGJyb2tlckhvc3RuYW1lLFxuICAgIGNsaWVudElkLFxuICAgIHBhc3N3b3JkLFxuICAgIHVzZXJuYW1lLFxuICAgIG5ldGtleSxcbiAgICBwb3J0LFxuICAgIHNlcmlhbE51bWJlcixcbiAgfToge1xuICAgIGJyb2tlckhvc3RuYW1lOiBzdHJpbmc7XG4gICAgc2VyaWFsTnVtYmVyOiBzdHJpbmc7XG4gICAgbmV0a2V5OiBzdHJpbmc7XG4gICAgcG9ydDogc3RyaW5nO1xuICAgIGNsaWVudElkPzogc3RyaW5nO1xuICAgIHVzZXJuYW1lPzogc3RyaW5nO1xuICAgIHBhc3N3b3JkPzogc3RyaW5nO1xuICB9KTogUHJvdG9jb2xNZXRhIHwgUHJvbWlzZUxpa2U8UHJvdG9jb2xNZXRhIHwgdW5kZWZpbmVkPiB8IHVuZGVmaW5lZCB7XG4gICAgY2xpZW50SWQgPVxuICAgICAgY2xpZW50SWQgfHwgYCR7c2VyaWFsTnVtYmVyfV8ke01hdGgucm91bmQoTWF0aC5yYW5kb20oKSAqIDEwMDAwMDApfWA7XG5cbiAgICB1c2VybmFtZSA9IHRoaXMuY29uZmlnLm1xdHQudXNlcm5hbWUgfHwgdXNlcm5hbWU7XG4gICAgcGFzc3dvcmQgPSB0aGlzLmNvbmZpZy5tcXR0LnBhc3N3b3JkIHx8IHBhc3N3b3JkO1xuXG4gICAgbGV0IGJyb2tlclBvcnQ6IHN0cmluZztcbiAgICBpZiAodGhpcy5wbGF0Zm9ybS5pcygnbW9iaWxld2ViJykpIHtcbiAgICAgIGlmICghdGhpcy5jb25maWcubXF0dC53ZWJTb2NrZXQ/LnBvcnQpIHtcbiAgICAgICAgZGVidWcoXG4gICAgICAgICAgVEFHLFxuICAgICAgICAgICdNUVRUIG92ZXIgd2ViIHNvY2tldCBpcyBub3QgYXZhaWxhYmxlIChubyBwb3J0IGNvbmZpZ3VyZWQuIFNraXBwaW5nLi4uKSdcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgIH1cbiAgICAgIGJyb2tlclBvcnQgPSB0aGlzLmNvbmZpZy5tcXR0LndlYlNvY2tldC5wb3J0LnRvU3RyaW5nKCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGJyb2tlclBvcnQgPSBwb3J0O1xuICAgIH1cbiAgICBsZXQgdXJsOiBzdHJpbmc7XG4gICAgaWYgKCFicm9rZXJIb3N0bmFtZS5tYXRjaCgvXlthLXpdKzpcXC9cXC8vaSkpIHtcbiAgICAgIGxldCBtcXR0UHJvdG9jb2wgPSB0aGlzLnBsYXRmb3JtLmlzKCdtb2JpbGV3ZWInKSA/ICd3cycgOiAnbXF0dCc7XG4gICAgICBpZiAoTVFUVF9TRUNVUkVfUE9SVFMuaW5jbHVkZXMocG9ydCkpIHtcbiAgICAgICAgbXF0dFByb3RvY29sICs9ICdzJztcbiAgICAgIH1cbiAgICAgIHVybCA9IGAke21xdHRQcm90b2NvbH06Ly8ke2Jyb2tlckhvc3RuYW1lfWA7XG4gICAgfSBlbHNlIHtcbiAgICAgIHVybCA9IGJyb2tlckhvc3RuYW1lO1xuICAgICAgaWYgKHRoaXMucGxhdGZvcm0uaXMoJ21vYmlsZXdlYicpKSB7XG4gICAgICAgIHVybCA9IHVybC5yZXBsYWNlKC9eKG1xdHQpcz86XFwvXFwvL2l1LCAnd3MnKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHtcbiAgICAgIHR5cGU6ICdtcXR0JyxcbiAgICAgIGluZm86IHtcbiAgICAgICAgdXNlcm5hbWUsXG4gICAgICAgIHBhc3N3b3JkLFxuICAgICAgICBzZXJpYWxOdW1iZXIsXG4gICAgICAgIG5ldGtleSxcbiAgICAgICAgY2xpZW50SWQsXG4gICAgICAgIGVuZHBvaW50OiBgJHt1cmx9OiR7YnJva2VyUG9ydH1gLFxuICAgICAgfSxcbiAgICB9O1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBfcmVzb2x2ZUNvbm5lY3Rpb25Qcm90b2NvbHMoXG4gICAgYXZhaWxhYmxlSG9zdFByb3RvY29sczogSG9zdFByb3RvY29sW10sXG4gICAgY3VycmVudEhvc3RQcm90b2NvbDogSG9zdFByb3RvY29sXG4gICkge1xuICAgIGRlYnVnKFxuICAgICAgVEFHLFxuICAgICAgYGF2YWlsYWJsZUhvc3RQcm90b2NvbHMgJHthdmFpbGFibGVIb3N0UHJvdG9jb2xzXG4gICAgICAgIC5tYXAoKHByb3RvY29sKSA9PiBIb3N0UHJvdG9jb2xbcHJvdG9jb2xdKVxuICAgICAgICAuam9pbignLCAnKX0uLi5gXG4gICAgKTtcbiAgICBjb25zdCByZXN1bHRzOiBQcm90b2NvbE1ldGFbXSA9IFtdO1xuICAgIGZvciAoY29uc3QgcHJvdG9jb2wgb2YgYXZhaWxhYmxlSG9zdFByb3RvY29scykge1xuICAgICAgaWYgKHByb3RvY29sID09PSBIb3N0UHJvdG9jb2wuTkZDIHx8IHByb3RvY29sID09PSBjdXJyZW50SG9zdFByb3RvY29sKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgY29ubmVjdGlvbkluZm8gPSBhd2FpdCB0aGlzLl9yZXNvbHZlQ29ubmVjdGlvblByb3RvY29sKHByb3RvY29sKTtcbiAgICAgICAgaWYgKGNvbm5lY3Rpb25JbmZvKSB7XG4gICAgICAgICAgcmVzdWx0cy5wdXNoKGNvbm5lY3Rpb25JbmZvKTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGNvbnNvbGUud2FybihgQ2Fubm90IHJlc29sdmUgcHJvdG9jb2wgJHtIb3N0UHJvdG9jb2xbcHJvdG9jb2xdfWApO1xuICAgICAgICAvLyBUT0RPIGxvZyA/XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXN1bHRzO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBfcmVzb2x2ZUNvbm5lY3Rpb25Qcm90b2NvbChcbiAgICBwcm90b2NvbDogSG9zdFByb3RvY29sXG4gICk6IFByb21pc2U8UHJvdG9jb2xNZXRhIHwgdW5kZWZpbmVkPiB7XG4gICAgZGVidWcoVEFHLCBgX3Jlc29sdmVDb25uZWN0aW9uUHJvdG9jb2wgJHtIb3N0UHJvdG9jb2xbcHJvdG9jb2xdfS4uLmApO1xuICAgIHN3aXRjaCAocHJvdG9jb2wpIHtcbiAgICAgIGNhc2UgSG9zdFByb3RvY29sLkJMRTpcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB0eXBlOiAnYmxlJyxcbiAgICAgICAgICBpbmZvOiB7XG4gICAgICAgICAgICBtYWM6IGF3YWl0IHRoaXMuaW5mb1Jlc29sdmVyLmdldFZhbHVlPHN0cmluZz4oXG4gICAgICAgICAgICAgIFRhcEluZm8uYmxlTWFjQWRkcmVzc1xuICAgICAgICAgICAgKSxcbiAgICAgICAgICB9LFxuICAgICAgICB9O1xuICAgICAgY2FzZSBIb3N0UHJvdG9jb2wuTVFUVDpcbiAgICAgICAgbGV0IFtcbiAgICAgICAgICBicm9rZXJIb3N0bmFtZSxcbiAgICAgICAgICBzZXJpYWxOdW1iZXIsXG4gICAgICAgICAgbmV0a2V5LFxuICAgICAgICAgIG1xdHRVc2VybmFtZSxcbiAgICAgICAgICBtcXR0UGFzc3dvcmQsXG4gICAgICAgICAgcG9ydCxcbiAgICAgICAgICBjbGllbnRJZCxcbiAgICAgICAgXSA9IGF3YWl0IHRoaXMuaW5mb1Jlc29sdmVyLmdldFZhbHVlczxcbiAgICAgICAgICBbc3RyaW5nLCBzdHJpbmcsIHN0cmluZywgc3RyaW5nLCBzdHJpbmcsIHN0cmluZywgc3RyaW5nXVxuICAgICAgICA+KFtcbiAgICAgICAgICBUYXBJbmZvLk1xdHRCcm9rZXJIb3N0bmFtZSxcbiAgICAgICAgICBUYXBJbmZvLnNlcmlhbE51bWJlcixcbiAgICAgICAgICBUYXBJbmZvLk1xdHRSZWxheU5ldEtleSxcbiAgICAgICAgICBUYXBJbmZvLk1xdHRVc2VybmFtZSxcbiAgICAgICAgICBUYXBJbmZvLk1xdHRQYXNzd29yZCxcbiAgICAgICAgICBUYXBJbmZvLk1xdHRCcm9rZXJQb3J0LFxuICAgICAgICAgIFRhcEluZm8uTXF0dENsaWVudElkLFxuICAgICAgICBdKTtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2NyZWF0ZU1xdHRQcm90b2NvbEZyb21UYXBJbmZvKHtcbiAgICAgICAgICBicm9rZXJIb3N0bmFtZSxcbiAgICAgICAgICBzZXJpYWxOdW1iZXIsXG4gICAgICAgICAgbmV0a2V5LFxuICAgICAgICAgIHVzZXJuYW1lOiBtcXR0VXNlcm5hbWUsXG4gICAgICAgICAgcGFzc3dvcmQ6IG1xdHRQYXNzd29yZCxcbiAgICAgICAgICBwb3J0LFxuICAgICAgICAgIGNsaWVudElkLFxuICAgICAgICB9KTtcbiAgICAgIGNhc2UgSG9zdFByb3RvY29sLldJRkk6XG4gICAgICAgIGNvbnN0IFt3aWZpTW9kZSwgd2lmaUhvc3RuYW1lLCB3aWZpU1NJRCwgd2lmaUtleV0gPVxuICAgICAgICAgIGF3YWl0IHRoaXMuaW5mb1Jlc29sdmVyLmdldFZhbHVlczxbV2lmaU1vZGUsIHN0cmluZywgc3RyaW5nLCBzdHJpbmddPihcbiAgICAgICAgICAgIFtcbiAgICAgICAgICAgICAgVGFwSW5mby5XaWZpTW9kZSxcbiAgICAgICAgICAgICAgVGFwSW5mby5XaWZpSG9zdG5hbWUsXG4gICAgICAgICAgICAgIFRhcEluZm8uV2lmaVNTSUQsXG4gICAgICAgICAgICAgIFRhcEluZm8uV2lmaUtleSxcbiAgICAgICAgICAgIF1cbiAgICAgICAgICApO1xuICAgICAgICBpZiAod2lmaUhvc3RuYW1lICYmIHdpZmlIb3N0bmFtZSAhPT0gJzAuMC4wLjAnKSB7XG4gICAgICAgICAgZGVidWcoXG4gICAgICAgICAgICBUQUcsXG4gICAgICAgICAgICBgcmVzb2x2ZUNvbm5lY3Rpb25Qcm90b2NvbCB3aWZpIG1vZGUgJHtXaWZpTW9kZVt3aWZpTW9kZV19Li4uYFxuICAgICAgICAgICk7XG4gICAgICAgICAgc3dpdGNoICh3aWZpTW9kZSkge1xuICAgICAgICAgICAgY2FzZSBXaWZpTW9kZS5ORVRXT1JLOlxuICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIHR5cGU6ICdzb2NrZXQnLFxuICAgICAgICAgICAgICAgIGluZm86IHtcbiAgICAgICAgICAgICAgICAgIHVybDogYHRjcDovLyR7d2lmaUhvc3RuYW1lfToyMDAwYCxcbiAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgY2FzZSBXaWZpTW9kZS5QRUVSX1RPX1BFRVI6XG4gICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgdHlwZTogJ3dpZmknLFxuICAgICAgICAgICAgICAgIGluZm86IHtcbiAgICAgICAgICAgICAgICAgIHNzaWQ6IHdpZmlTU0lELFxuICAgICAgICAgICAgICAgICAgdXJsOiBgdGNwOi8vJHt3aWZpSG9zdG5hbWV9OjIwMDBgLFxuICAgICAgICAgICAgICAgICAgcGFzc3dvcmQ6IHdpZmlLZXksXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICBkZWZhdWx0OlxuICAgICAgICBkZWJ1ZyhcbiAgICAgICAgICBUQUcsXG4gICAgICAgICAgYHJlc29sdmVDb25uZWN0aW9uUHJvdG9jb2wgbm90aGluZyB0byBzdG9yZSBmb3IgcHJvdG9jb2wgJHtIb3N0UHJvdG9jb2xbcHJvdG9jb2xdfWBcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG4gIH1cblxuICBhZGRPclJlZnJlc2gobmV3SXRlbTogUGljazxUYXBDb25uZWN0aW9uU3RvcmVJdGVtLCAnY29ubmVjdGlvbicgfCAnY29uZmlnJz4pIHtcbiAgICBkZWJ1ZyhcbiAgICAgIGAke1RBR30gQWRkaW5nL3VwZGF0aW5nIG5ldyBzdG9yZWQgZGV2aWNlICR7SlNPTi5zdHJpbmdpZnkobmV3SXRlbSl9Li4uYFxuICAgICk7XG4gICAgY29uc3QgZXhpc3RpbmdJdGVtID0gdGhpcy5pdGVtcy5maW5kKChpdGVtKSA9PiB7XG4gICAgICByZXR1cm4gKFxuICAgICAgICBpdGVtLmNvbmZpZy5zZXJpYWxOdW1iZXIgPT09IG5ld0l0ZW0uY29uZmlnLnNlcmlhbE51bWJlciAmJlxuICAgICAgICBpdGVtLmNvbm5lY3Rpb24udHlwZSA9PT0gbmV3SXRlbS5jb25uZWN0aW9uLnR5cGVcbiAgICAgICk7XG4gICAgfSk7XG4gICAgaWYgKGV4aXN0aW5nSXRlbSkge1xuICAgICAgZXhpc3RpbmdJdGVtLmNvbmZpZyA9IG5ld0l0ZW0uY29uZmlnO1xuICAgICAgZXhpc3RpbmdJdGVtLmNvbm5lY3Rpb24gPSBuZXdJdGVtLmNvbm5lY3Rpb247XG4gICAgICBleGlzdGluZ0l0ZW0ubWV0YS5sYXN0Q29ubmVjdGlvbiA9IG5ldyBEYXRlKCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuaXRlbXMucHVzaCh7XG4gICAgICAgIGNvbm5lY3Rpb246IG5ld0l0ZW0uY29ubmVjdGlvbixcbiAgICAgICAgY29uZmlnOiBuZXdJdGVtLmNvbmZpZyxcbiAgICAgICAgbWV0YToge1xuICAgICAgICAgIHN0YXRlOiAnb25saW5lJyxcbiAgICAgICAgICBsYXN0Q29ubmVjdGlvbjogbmV3IERhdGUoKSxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuICAgIH1cbiAgICBkZWJ1ZyhcbiAgICAgIGAke1RBR30gQWRkZWQvdXBkYXRlZCBuZXcgc3RvcmVkIGRldmljZSAke25ld0l0ZW0uY29uZmlnLnNlcmlhbE51bWJlcn0gJHtuZXdJdGVtLmNvbm5lY3Rpb24udHlwZX1gXG4gICAgKTtcbiAgICB0aGlzLnNldCh0aGlzLml0ZW1zKTtcbiAgfVxuXG4gIGNsZWFyKCkge1xuICAgIHRoaXMuc2V0KFtdKTtcbiAgfVxuXG4gIHNhdmVUb1N0b3JhZ2UoKSB7XG4gICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0oXG4gICAgICB0aGlzLmNvbmZpZy5sb2NhbFN0b3JhZ2VLZXksXG4gICAgICBKU09OLnN0cmluZ2lmeSh0aGlzLml0ZW1zKVxuICAgICk7XG4gIH1cblxuICBsb2FkRnJvbVN0b3JhZ2UoKSB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGNvbnRlbnQgPSBsb2NhbFN0b3JhZ2UuZ2V0SXRlbSh0aGlzLmNvbmZpZy5sb2NhbFN0b3JhZ2VLZXkpO1xuICAgICAgaWYgKGNvbnRlbnQpIHtcbiAgICAgICAgdGhpcy5zZXQoSlNPTi5wYXJzZShjb250ZW50KSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLnNldChbXSk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBjb25zb2xlLndhcm4oJ0Nhbm5vdCBsb2FkIHN0b3JlZCBjb25uZWN0aW9uIGZyb20gbG9jYWwgc3RvcmFnZScsIGVycik7XG4gICAgICB0aGlzLnNldChbXSk7XG4gICAgfVxuICB9XG59XG4iXX0=