UNPKG

@chatie/angular

Version:

Wechaty Component NgModule

356 lines 44.6 kB
import { __awaiter } from "tslib"; import { BehaviorSubject, Observable, Subject, } from 'rxjs'; import { filter, share, } from 'rxjs/operators'; import { Brolog } from 'brolog'; import { StateSwitch } from 'state-switch'; export var ReadyState; (function (ReadyState) { ReadyState[ReadyState["CLOSED"] = WebSocket.CLOSED] = "CLOSED"; ReadyState[ReadyState["CLOSING"] = WebSocket.CLOSING] = "CLOSING"; ReadyState[ReadyState["CONNECTING"] = WebSocket.CONNECTING] = "CONNECTING"; ReadyState[ReadyState["OPEN"] = WebSocket.OPEN] = "OPEN"; })(ReadyState || (ReadyState = {})); export class IoService { constructor() { this.autoReconnect = true; this.log = Brolog.instance(); this.CONNECT_TIMEOUT = 10 * 1000; // 10 seconds this.ENDPOINT = 'wss://api.chatie.io/v0/websocket/token/'; this.PROTOCOL = 'web|0.0.1'; this.sendBuffer = []; this.log.verbose('IoService', 'constructor()'); } get readyState() { return this._readyState.asObservable(); } init() { return __awaiter(this, void 0, void 0, function* () { this.log.verbose('IoService', 'init()'); if (this.state) { throw new Error('re-init'); } this.snapshot = { readyState: ReadyState.CLOSED, event: null, }; this._readyState = new BehaviorSubject(ReadyState.CLOSED); this.state = new StateSwitch('IoService', this.log); this.state.setLog(this.log); try { yield this.initStateDealer(); yield this.initRxSocket(); } catch (e) { this.log.silly('IoService', 'init() exception: %s', e.message); throw e; } this.readyState.subscribe(s => { this.log.silly('IoService', 'init() readyState.subscribe(%s)', ReadyState[s]); this.snapshot.readyState = s; }); // IMPORTANT: subscribe to event and make it HOT! this.event.subscribe(s => { this.log.silly('IoService', 'init() event.subscribe({name:%s})', s.name); this.snapshot.event = s; }); return; }); } token(newToken) { this.log.silly('IoService', 'token(%s)', newToken); if (newToken) { this._token = newToken; return; } return this._token; } start() { return __awaiter(this, void 0, void 0, function* () { this.log.verbose('IoService', 'start() with token:%s', this._token); if (!this._token) { throw new Error('start() without token'); } if (this.state.on()) { throw new Error('state is already ON'); } if (this.state.pending()) { throw new Error('state is pending'); } this.state.on('pending'); this.autoReconnect = true; try { yield this.connectRxSocket(); this.state.on(true); } catch (e) { this.log.warn('IoService', 'start() failed:%s', e.message); this.state.off(true); } }); } stop() { return __awaiter(this, void 0, void 0, function* () { this.log.verbose('IoService', 'stop()'); if (this.state.off()) { this.log.warn('IoService', 'stop() state is already off'); if (this.state.pending()) { throw new Error('state pending() is true'); } return; } this.state.off('pending'); this.autoReconnect = false; if (!this._websocket) { throw new Error('no websocket'); } yield this.socketClose(1000, 'IoService.stop()'); this.state.off(true); return; }); } restart() { return __awaiter(this, void 0, void 0, function* () { this.log.verbose('IoService', 'restart()'); try { yield this.stop(); yield this.start(); } catch (e) { this.log.error('IoService', 'restart() error:%s', e.message); throw e; } return; }); } initStateDealer() { this.log.verbose('IoService', 'initStateDealer()'); const isReadyStateOpen = (s) => s === ReadyState.OPEN; this.readyState.pipe(filter(isReadyStateOpen)) .subscribe(open => this.stateOnOpen()); } /** * Creates a subject from the specified observer and observable. * - https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/subject.md * Create an Rx.Subject using Subject.create that allows onNext without subscription * A socket implementation (example, don't use) * - http://stackoverflow.com/a/34862286/1123955 */ initRxSocket() { return __awaiter(this, void 0, void 0, function* () { this.log.verbose('IoService', 'initRxSocket()'); if (this.event) { throw new Error('re-init is not permitted'); } // 1. Mobile Originated. moObserver.next() means mobile is sending this.moObserver = { next: this.socketSend.bind(this), error: this.socketClose.bind(this), complete: this.socketClose.bind(this), }; // 2. Mobile Terminated. mtObserver.next() means mobile is receiving const observable = new Observable((observer) => { this.log.verbose('IoService', 'initRxSocket() Observable.create()'); this.mtObserver = observer; return this.socketClose.bind(this); }); // 3. Subject for MO & MT Observers this.event = Subject.create(this.moObserver, observable.pipe(share())); }); } connectRxSocket() { return __awaiter(this, void 0, void 0, function* () { this.log.verbose('IoService', 'connectRxSocket()'); // FIXME: check & close the old one if (this._websocket) { throw new Error('already has a websocket'); } // if (this.state.target() !== 'open' // || this.state.current() !== 'open' // || this.state.stable() if (this.state.off()) { throw new Error('switch state is off'); } else if (!this.state.pending()) { throw new Error('switch state is already ON'); } this._websocket = new WebSocket(this.endPoint(), this.PROTOCOL); this.socketUpdateState(); const onOpenPromise = new Promise((resolve, reject) => { this.log.verbose('IoService', 'connectRxSocket() Promise()'); const id = setTimeout(() => { this._websocket = null; const e = new Error('rxSocket connect timeout after ' + Math.round(this.CONNECT_TIMEOUT / 1000)); reject(e); }, this.CONNECT_TIMEOUT); // timeout for connect websocket this._websocket.onopen = (e) => { this.log.verbose('IoService', 'connectRxSocket() Promise() WebSocket.onOpen() resolve()'); this.socketUpdateState(); clearTimeout(id); resolve(); }; }); // Handle the payload this._websocket.onmessage = this.socketOnMessage.bind(this); // Deal the event this._websocket.onerror = this.socketOnError.bind(this); this._websocket.onclose = this.socketOnClose.bind(this); return onOpenPromise; }); } endPoint() { const url = this.ENDPOINT + this._token; this.log.verbose('IoService', 'endPoint() => %s', url); return url; } /****************************************************************** * * State Event Listeners * */ stateOnOpen() { this.log.verbose('IoService', 'stateOnOpen()'); this.socketSendBuffer(); this.rpcUpdate('from stateOnOpen()'); } /****************************************************************** * * Io RPC Methods * */ rpcDing(payload) { return __awaiter(this, void 0, void 0, function* () { this.log.verbose('IoService', 'ding(%s)', payload); const e = { name: 'ding', payload, }; this.event.next(e); // TODO: get the return value }); } rpcUpdate(payload) { return __awaiter(this, void 0, void 0, function* () { this.event.next({ name: 'update', payload, }); }); } /****************************************************************** * * Socket Actions * */ socketClose(code, reason) { return __awaiter(this, void 0, void 0, function* () { this.log.verbose('IoService', 'socketClose()'); if (!this._websocket) { throw new Error('no websocket'); } this._websocket.close(code, reason); this.socketUpdateState(); const future = new Promise(resolve => { this.readyState.pipe(filter(s => s === ReadyState.CLOSED)) .subscribe(resolve); }); yield future; return; }); } socketSend(ioEvent) { this.log.silly('IoService', 'socketSend({name:%s, payload:%s})', ioEvent.name, ioEvent.payload); if (!this._websocket) { this.log.silly('IoService', 'socketSend() no _websocket'); } const strEvt = JSON.stringify(ioEvent); this.sendBuffer.push(strEvt); // XXX can move this to onOpen? this.socketSendBuffer(); } socketSendBuffer() { this.log.silly('IoService', 'socketSendBuffer() length:%s', this.sendBuffer.length); if (!this._websocket) { throw new Error('socketSendBuffer(): no _websocket'); } if (this._websocket.readyState !== WebSocket.OPEN) { this.log.warn('IoService', 'socketSendBuffer() readyState is not OPEN, send job delayed.'); return; } while (this.sendBuffer.length) { const buf = this.sendBuffer.shift(); this.log.silly('IoService', 'socketSendBuffer() sending(%s)', buf); this._websocket.send(buf); } } socketUpdateState() { var _a; this.log.verbose('IoService', 'socketUpdateState() is %s', ReadyState[(_a = this._websocket) === null || _a === void 0 ? void 0 : _a.readyState]); if (!this._websocket) { this.log.error('IoService', 'socketUpdateState() no _websocket'); return; } this._readyState.next(this._websocket.readyState); } /****************************************************************** * * Socket Events Listener * */ socketOnMessage(message) { this.log.verbose('IoService', 'onMessage({data: %s})', message.data); const data = message.data; // WebSocket data const ioEvent = { name: 'raw', payload: data, }; // this is default io event for unknown format message try { const obj = JSON.parse(data); ioEvent.name = obj.name; ioEvent.payload = obj.payload; } catch (e) { this.log.warn('IoService', 'onMessage parse message fail. save as RAW'); } this.mtObserver.next(ioEvent); } socketOnError(event) { this.log.silly('IoService', 'socketOnError(%s)', event); // this._websocket = null } /** * https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent * code: 1006 CLOSE_ABNORMAL * - Reserved. Used to indicate that a connection was closed abnormally * (that is, with no close frame being sent) when a status code is expected. */ socketOnClose(closeEvent) { this.log.verbose('IoService', 'socketOnClose({code:%s, reason:%s, returnValue:%s})', closeEvent.code, closeEvent.reason, closeEvent.returnValue); this.socketUpdateState(); /** * reconnect inside onClose */ if (this.autoReconnect) { this.state.on('pending'); setTimeout(() => __awaiter(this, void 0, void 0, function* () { try { yield this.connectRxSocket(); this.state.on(true); } catch (e) { this.log.warn('IoService', 'socketOnClose() autoReconnect() exception: %s', e); this.state.off(true); } }), 1000); } else { this.state.off(true); } this._websocket = null; if (!closeEvent.wasClean) { this.log.warn('IoService', 'socketOnClose() event.wasClean FALSE'); // TODO emit error } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW8uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvd2VjaGF0eS9pby50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBRUEsT0FBTyxFQUNMLGVBQWUsRUFDZixVQUFVLEVBRVYsT0FBTyxHQUNSLE1BQXdCLE1BQU0sQ0FBQTtBQUMvQixPQUFPLEVBQ0wsTUFBTSxFQUNOLEtBQUssR0FDTixNQUF3QixnQkFBZ0IsQ0FBQTtBQUV6QyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQVksUUFBUSxDQUFBO0FBQ3JDLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTyxjQUFjLENBQUE7QUFzQjNDLE1BQU0sQ0FBTixJQUFZLFVBS1g7QUFMRCxXQUFZLFVBQVU7SUFDcEIsa0NBQWMsU0FBUyxDQUFDLE1BQU0sWUFBQSxDQUFBO0lBQzlCLG1DQUFjLFNBQVMsQ0FBQyxPQUFPLGFBQUEsQ0FBQTtJQUMvQixzQ0FBYyxTQUFTLENBQUMsVUFBVSxnQkFBQSxDQUFBO0lBQ2xDLGdDQUFjLFNBQVMsQ0FBQyxJQUFJLFVBQUEsQ0FBQTtBQUM5QixDQUFDLEVBTFcsVUFBVSxLQUFWLFVBQVUsUUFLckI7QUFPRCxNQUFNLE9BQU8sU0FBUztJQTBCcEI7UUFmUSxrQkFBYSxHQUFHLElBQUksQ0FBQTtRQUNwQixRQUFHLEdBQUcsTUFBTSxDQUFDLFFBQVEsRUFBRSxDQUFBO1FBRWQsb0JBQWUsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFBLENBQUMsYUFBYTtRQUN6QyxhQUFRLEdBQUcseUNBQXlDLENBQUE7UUFDcEQsYUFBUSxHQUFHLFdBQVcsQ0FBQTtRQU0vQixlQUFVLEdBQWEsRUFBRSxDQUFBO1FBSy9CLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLFdBQVcsRUFBRSxlQUFlLENBQUMsQ0FBQTtJQUNoRCxDQUFDO0lBdkJELElBQVcsVUFBVTtRQUNuQixPQUFPLElBQUksQ0FBQyxXQUFXLENBQUMsWUFBWSxFQUFFLENBQUE7SUFDeEMsQ0FB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LENBQUMsQ0FBQTtZQUN4QixVQUFVLENBQUMsR0FBUyxFQUFFO2dCQUNwQixJQUFJO29CQUNGLE1BQU0sSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFBO29CQUM1QixJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQTtpQkFDcEI7Z0JBQUMsT0FBTyxDQUFDLEVBQUU7b0JBQ1YsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLCtDQUErQyxFQUFFLENBQUMsQ0FBQyxDQUFBO29CQUM5RSxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQTtpQkFDckI7WUFDSCxDQUFDLENBQUEsRUFBRSxJQUFJLENBQUMsQ0FBQTtTQUNUO2FBQU07WUFDTCxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQTtTQUNyQjtRQUNELElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFBO1FBRXRCLElBQUksQ0FBQyxVQUFVLENBQUMsUUFBUSxFQUFFO1lBQ3hCLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxzQ0FBc0MsQ0FBQyxDQUFBO1lBQ2xFLGtCQUFrQjtTQUNuQjtJQUNILENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFZFUlNJT04gfSAgICAgIGZyb20gJy4uL2NvbmZpZydcblxuaW1wb3J0IHtcbiAgQmVoYXZpb3JTdWJqZWN0LFxuICBPYnNlcnZhYmxlLFxuICBPYnNlcnZlcixcbiAgU3ViamVjdCxcbn0gICAgICAgICAgICAgICAgICAgZnJvbSAncnhqcydcbmltcG9ydCB7XG4gIGZpbHRlcixcbiAgc2hhcmUsXG59ICAgICAgICAgICAgICAgICAgIGZyb20gJ3J4anMvb3BlcmF0b3JzJ1xuXG5pbXBvcnQgeyBCcm9sb2cgfSAgICAgICBmcm9tICdicm9sb2cnXG5pbXBvcnQgeyBTdGF0ZVN3aXRjaCB9ICBmcm9tICdzdGF0ZS1zd2l0Y2gnXG5cbmV4cG9ydCB0eXBlIFdlY2hhdHlFdmVudE5hbWUgPVxuICAgICdzY2FuJ1xuICB8ICdsb2dpbicgfCAnbG9nb3V0J1xuICB8ICdyZXNldCcgfCAnc2h1dGRvd24nXG4gIHwgJ2RpbmcnICB8ICdkb25nJ1xuICB8ICdtZXNzYWdlJ1xuICB8ICdoZWFydGJlYXQnXG4gIHwgJ3VwZGF0ZSdcbiAgfCAnZXJyb3InXG5cbmV4cG9ydCB0eXBlIFNlcnZlckV2ZW50TmFtZSA9ICdzeXMnXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAnYm90aWUnXG5cbmV4cG9ydCB0eXBlIElvRXZlbnROYW1lID0gJ3JhdycgfCBXZWNoYXR5RXZlbnROYW1lIHwgU2VydmVyRXZlbnROYW1lXG5cbmV4cG9ydCBpbnRlcmZhY2UgSW9FdmVudCB7XG4gIG5hbWU6IElvRXZlbnROYW1lLFxuICBwYXlsb2FkOiBhbnksXG59XG5cbmV4cG9ydCBlbnVtIFJlYWR5U3RhdGUge1xuICBDTE9TRUQgICAgICA9IFdlYlNvY2tldC5DTE9TRUQsXG4gIENMT1NJTkcgICAgID0gV2ViU29ja2V0LkNMT1NJTkcsXG4gIENPTk5FQ1RJTkcgID0gV2ViU29ja2V0LkNPTk5FQ1RJTkcsXG4gIE9QRU4gICAgICAgID0gV2ViU29ja2V0Lk9QRU4sXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgSW9TZXJ2aWNlU25hcHNob3Qge1xuICByZWFkeVN0YXRlOiBSZWFkeVN0YXRlXG4gIGV2ZW50OiAgICAgSW9FdmVudFxufVxuXG5leHBvcnQgY2xhc3MgSW9TZXJ2aWNlIHtcbiAgLy8gaHR0cHM6Ly9naXRodWIuY29tL1JlYWN0aXZlWC9yeGpzL2Jsb2IvbWFzdGVyL3NyYy9vYnNlcnZhYmxlL2RvbS9XZWJTb2NrZXRTdWJqZWN0LnRzXG4gIHB1YmxpYyBldmVudDogU3ViamVjdDxJb0V2ZW50PlxuXG4gIHByaXZhdGUgX3JlYWR5U3RhdGU6IEJlaGF2aW9yU3ViamVjdDxSZWFkeVN0YXRlPlxuICBwdWJsaWMgZ2V0IHJlYWR5U3RhdGUoKSB7XG4gICAgcmV0dXJuIHRoaXMuX3JlYWR5U3RhdGUuYXNPYnNlcnZhYmxlKClcbiAgfVxuXG4gIHB1YmxpYyBzbmFwc2hvdDogSW9TZXJ2aWNlU25hcHNob3RcblxuICBwcml2YXRlIGF1dG9SZWNvbm5lY3QgPSB0cnVlXG4gIHByaXZhdGUgbG9nID0gQnJvbG9nLmluc3RhbmNlKClcblxuICBwcml2YXRlIHJlYWRvbmx5IENPTk5FQ1RfVElNRU9VVCA9IDEwICogMTAwMCAvLyAxMCBzZWNvbmRzXG4gIHByaXZhdGUgcmVhZG9ubHkgRU5EUE9JTlQgPSAnd3NzOi8vYXBpLmNoYXRpZS5pby92MC93ZWJzb2NrZXQvdG9rZW4vJ1xuICBwcml2YXRlIHJlYWRvbmx5IFBST1RPQ09MID0gJ3dlYnwwLjAuMSdcblxuICBwcml2YXRlIF90b2tlbjogc3RyaW5nIC8vIEZJWE1FIHBvc3NpYmxlIGJlIGB1bmRlZmluZWRgXG4gIHByaXZhdGUgX3dlYnNvY2tldDogV2ViU29ja2V0IHwgbnVsbFxuICBwcml2YXRlIG1vT2JzZXJ2ZXI6IE9ic2VydmVyPElvRXZlbnQ+IC8vIE1vYmlsZSBPcmlnaW5hdGVkLiBtb09ic2VydmVyLm5leHQoKSBtZWFucyBtb2JpbGUgaXMgc2VuZGluZ1xuICBwcml2YXRlIG10T2JzZXJ2ZXI6IE9ic2VydmVyPElvRXZlbnQ+IC8vIE1vYmlsZSBUZXJtaW5hdGVkLiBtdE9ic2VydmVyLm5leHQoKSBtZWFucyBtb2JpbGUgaXMgcmVjZWl2aW5nXG4gIHByaXZhdGUgc2VuZEJ1ZmZlcjogc3RyaW5nW10gPSBbXVxuXG4gIHByaXZhdGUgc3RhdGU6IFN0YXRlU3dpdGNoXG5cbiAgY29uc3RydWN0b3IoKSB7XG4gICAgdGhpcy5sb2cudmVyYm9zZSgnSW9TZXJ2aWNlJywgJ2NvbnN0cnVjdG9yKCknKVxuICB9XG5cbiAgcHVibGljIGFzeW5jIGluaXQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5sb2cudmVyYm9zZSgnSW9TZXJ2aWNlJywgJ2luaXQoKScpXG5cbiAgICBpZiAodGhpcy5zdGF0ZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdyZS1pbml0JylcbiAgICB9XG5cbiAgICB0aGlzLnNuYXBzaG90ID0ge1xuICAgICAgcmVhZHlTdGF0ZTogUmVhZHlTdGF0ZS5DTE9TRUQsXG4gICAgICBldmVudDogICAgIG51bGwsXG4gICAgfVxuXG4gICAgdGhpcy5fcmVhZHlTdGF0ZSA9IG5ldyBCZWhhdmlvclN1YmplY3Q8UmVhZHlTdGF0ZT4oUmVhZHlTdGF0ZS5DTE9TRUQpXG4gICAgdGhpcy5zdGF0ZSA9IG5ldyBTdGF0ZVN3aXRjaCgnSW9TZXJ2aWNlJywgdGhpcy5sb2cpXG4gICAgdGhpcy5zdGF0ZS5zZXRMb2codGhpcy5sb2cpXG5cbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5pbml0U3RhdGVEZWFsZXIoKVxuICAgICAgYXdhaXQgdGhpcy5pbml0UnhTb2NrZXQoKVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHRoaXMubG9nLnNpbGx5KCdJb1NlcnZpY2UnLCAnaW5pdCgpIGV4Y2VwdGlvbjogJXMnLCBlLm1lc3NhZ2UpXG4gICAgICB0aHJvdyBlXG4gICAgfVxuXG4gICAgdGhpcy5yZWFkeVN0YXRlLnN1YnNjcmliZShzID0+IHtcbiAgICAgIHRoaXMubG9nLnNpbGx5KCdJb1NlcnZpY2UnLCAnaW5pdCgpIHJlYWR5U3RhdGUuc3Vic2NyaWJlKCVzKScsIFJlYWR5U3RhdGVbc10pXG4gICAgICB0aGlzLnNuYXBzaG90LnJlYWR5U3RhdGUgPSBzXG4gICAgfSlcbiAgICAvLyBJTVBPUlRBTlQ6IHN1YnNjcmliZSB0byBldmVudCBhbmQgbWFrZSBpdCBIT1QhXG4gICAgdGhpcy5ldmVudC5zdWJzY3JpYmUocyA9PiB7XG4gICAgICB0aGlzLmxvZy5zaWxseSgnSW9TZXJ2aWNlJywgJ2luaXQoKSBldmVudC5zdWJzY3JpYmUoe25hbWU6JXN9KScsIHMubmFtZSlcbiAgICAgIHRoaXMuc25hcHNob3QuZXZlbnQgPSBzXG4gICAgfSlcblxuICAgIHJldHVyblxuICB9XG5cbiAgcHVibGljIHRva2VuKCk6IHN0cmluZ1xuICBwdWJsaWMgdG9rZW4obmV3VG9rZW46IHN0cmluZyk6IHZvaWRcblxuICBwdWJsaWMgdG9rZW4obmV3VG9rZW4/OiBzdHJpbmcpOiBzdHJpbmcgfCB2b2lkIHtcbiAgICB0aGlzLmxvZy5zaWxseSgnSW9TZXJ2aWNlJywgJ3Rva2VuKCVzKScsIG5ld1Rva2VuKVxuICAgIGlmIChuZXdUb2tlbikge1xuICAgICAgdGhpcy5fdG9rZW4gPSBuZXdUb2tlblxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIHJldHVybiB0aGlzLl90b2tlblxuICB9XG5cbiAgYXN5bmMgc3RhcnQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5sb2cudmVyYm9zZSgnSW9TZXJ2aWNlJywgJ3N0YXJ0KCkgd2l0aCB0b2tlbjolcycsIHRoaXMuX3Rva2VuKVxuXG4gICAgaWYgKCF0aGlzLl90b2tlbikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdzdGFydCgpIHdpdGhvdXQgdG9rZW4nKVxuICAgIH1cblxuICAgIGlmICh0aGlzLnN0YXRlLm9uKCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignc3RhdGUgaXMgYWxyZWFkeSBPTicpXG4gICAgfVxuICAgIGlmICh0aGlzLnN0YXRlLnBlbmRpbmcoKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdzdGF0ZSBpcyBwZW5kaW5nJylcbiAgICB9XG5cbiAgICB0aGlzLnN0YXRlLm9uKCdwZW5kaW5nJylcblxuICAgIHRoaXMuYXV0b1JlY29ubmVjdCA9IHRydWVcblxuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLmNvbm5lY3RSeFNvY2tldCgpXG4gICAgICB0aGlzLnN0YXRlLm9uKHRydWUpXG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgdGhpcy5sb2cud2FybignSW9TZXJ2aWNlJywgJ3N0YXJ0KCkgZmFpbGVkOiVzJywgZS5tZXNzYWdlKVxuXG4gICAgICB0aGlzLnN0YXRlLm9mZih0cnVlKVxuICAgIH1cbiAgfVxuXG4gIGFzeW5jIHN0b3AoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5sb2cudmVyYm9zZSgnSW9TZXJ2aWNlJywgJ3N0b3AoKScpXG5cbiAgICBpZiAodGhpcy5zdGF0ZS5vZmYoKSkge1xuICAgICAgdGhpcy5sb2cud2FybignSW9TZXJ2aWNlJywgJ3N0b3AoKSBzdGF0ZSBpcyBhbHJlYWR5IG9mZicpXG4gICAgICBpZiAodGhpcy5zdGF0ZS5wZW5kaW5nKCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdzdGF0ZSBwZW5kaW5nKCkgaXMgdHJ1ZScpXG4gICAgICB9XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICB0aGlzLnN0YXRlLm9mZigncGVuZGluZycpXG5cbiAgICB0aGlzLmF1dG9SZWNvbm5lY3QgPSBmYWxzZVxuXG4gICAgaWYgKCF0aGlzLl93ZWJzb2NrZXQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbm8gd2Vic29ja2V0JylcbiAgICB9XG5cbiAgICBhd2FpdCB0aGlzLnNvY2tldENsb3NlKDEwMDAsICdJb1NlcnZpY2Uuc3RvcCgpJylcbiAgICB0aGlzLnN0YXRlLm9mZih0cnVlKVxuXG4gICAgcmV0dXJuXG4gIH1cblxuICBwdWJsaWMgYXN5bmMgcmVzdGFydCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmxvZy52ZXJib3NlKCdJb1NlcnZpY2UnLCAncmVzdGFydCgpJylcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5zdG9wKClcbiAgICAgIGF3YWl0IHRoaXMuc3RhcnQoKVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHRoaXMubG9nLmVycm9yKCdJb1NlcnZpY2UnLCAncmVzdGFydCgpIGVycm9yOiVzJywgZS5tZXNzYWdlKVxuICAgICAgdGhyb3cgZVxuICAgIH1cbiAgICByZXR1cm5cbiAgfVxuXG4gIHByaXZhdGUgaW5pdFN0YXRlRGVhbGVyKCkge1xuICAgIHRoaXMubG9nLnZlcmJvc2UoJ0lvU2VydmljZScsICdpbml0U3RhdGVEZWFsZXIoKScpXG5cbiAgICBjb25zdCBpc1JlYWR5U3RhdGVPcGVuID0gKHM6IFJlYWR5U3RhdGUpID0+IHMgPT09IFJlYWR5U3RhdGUuT1BFTlxuXG4gICAgdGhpcy5yZWFkeVN0YXRlLnBpcGUoXG4gICAgICBmaWx0ZXIoaXNSZWFkeVN0YXRlT3BlbiksXG4gICAgKVxuICAgICAgLnN1YnNjcmliZShvcGVuID0+IHRoaXMuc3RhdGVPbk9wZW4oKSlcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgc3ViamVjdCBmcm9tIHRoZSBzcGVjaWZpZWQgb2JzZXJ2ZXIgYW5kIG9ic2VydmFibGUuXG4gICAqICAtIGh0dHBzOi8vZ2l0aHViLmNvbS9SZWFjdGl2ZS1FeHRlbnNpb25zL1J4SlMvYmxvYi9tYXN0ZXIvZG9jL2FwaS9zdWJqZWN0cy9zdWJqZWN0Lm1kXG4gICAqIENyZWF0ZSBhbiBSeC5TdWJqZWN0IHVzaW5nIFN1YmplY3QuY3JlYXRlIHRoYXQgYWxsb3dzIG9uTmV4dCB3aXRob3V0IHN1YnNjcmlwdGlvblxuICAgKiAgIEEgc29ja2V0IGltcGxlbWVudGF0aW9uIChleGFtcGxlLCBkb24ndCB1c2UpXG4gICAqICAtIGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9hLzM0ODYyMjg2LzExMjM5NTVcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgaW5pdFJ4U29ja2V0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMubG9nLnZlcmJvc2UoJ0lvU2VydmljZScsICdpbml0UnhTb2NrZXQoKScpXG5cbiAgICBpZiAodGhpcy5ldmVudCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdyZS1pbml0IGlzIG5vdCBwZXJtaXR0ZWQnKVxuICAgIH1cblxuICAgIC8vIDEuIE1vYmlsZSBPcmlnaW5hdGVkLiBtb09ic2VydmVyLm5leHQoKSBtZWFucyBtb2JpbGUgaXMgc2VuZGluZ1xuICAgIHRoaXMubW9PYnNlcnZlciA9IHtcbiAgICAgIG5leHQ6ICAgICB0aGlzLnNvY2tldFNlbmQuYmluZCh0aGlzKSxcbiAgICAgIGVycm9yOiAgICB0aGlzLnNvY2tldENsb3NlLmJpbmQodGhpcyksXG4gICAgICBjb21wbGV0ZTogdGhpcy5zb2NrZXRDbG9zZS5iaW5kKHRoaXMpLFxuICAgIH1cblxuICAgIC8vIDIuIE1vYmlsZSBUZXJtaW5hdGVkLiBtdE9ic2VydmVyLm5leHQoKSBtZWFucyBtb2JpbGUgaXMgcmVjZWl2aW5nXG4gICAgY29uc3Qgb2JzZXJ2YWJsZSA9IG5ldyBPYnNlcnZhYmxlKChvYnNlcnZlcjogT2JzZXJ2ZXI8SW9FdmVudD4pID0+IHtcbiAgICAgIHRoaXMubG9nLnZlcmJvc2UoJ0lvU2VydmljZScsICdpbml0UnhTb2NrZXQoKSBPYnNlcnZhYmxlLmNyZWF0ZSgpJylcbiAgICAgIHRoaXMubXRPYnNlcnZlciA9IG9ic2VydmVyXG5cbiAgICAgIHJldHVybiB0aGlzLnNvY2tldENsb3NlLmJpbmQodGhpcylcbiAgICB9KVxuXG4gICAgLy8gMy4gU3ViamVjdCBmb3IgTU8gJiBNVCBPYnNlcnZlcnNcbiAgICB0aGlzLmV2ZW50ID0gU3ViamVjdC5jcmVhdGUodGhpcy5tb09ic2VydmVyLCBvYnNlcnZhYmxlLnBpcGUoc2hhcmUoKSkpXG5cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgY29ubmVjdFJ4U29ja2V0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMubG9nLnZlcmJvc2UoJ0lvU2VydmljZScsICdjb25uZWN0UnhTb2NrZXQoKScpXG5cbiAgICAvLyBGSVhNRTogY2hlY2sgJiBjbG9zZSB0aGUgb2xkIG9uZVxuICAgIGlmICh0aGlzLl93ZWJzb2NrZXQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignYWxyZWFkeSBoYXMgYSB3ZWJzb2NrZXQnKVxuICAgIH1cblxuICAgIC8vIGlmICh0aGlzLnN0YXRlLnRhcmdldCgpICE9PSAnb3BlbidcbiAgICAvLyAgIHx8IHRoaXMuc3RhdGUuY3VycmVudCgpICE9PSAnb3BlbidcbiAgICAvLyAgIHx8IHRoaXMuc3RhdGUuc3RhYmxlKClcbiAgICBpZiAodGhpcy5zdGF0ZS5vZmYoKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdzd2l0Y2ggc3RhdGUgaXMgb2ZmJylcbiAgICB9IGVsc2UgaWYgKCF0aGlzLnN0YXRlLnBlbmRpbmcoKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdzd2l0Y2ggc3RhdGUgaXMgYWxyZWFkeSBPTicpXG4gICAgfVxuXG4gICAgdGhpcy5fd2Vic29ja2V0ID0gbmV3IFdlYlNvY2tldCh0aGlzLmVuZFBvaW50KCksIHRoaXMuUFJPVE9DT0wpXG4gICAgdGhpcy5zb2NrZXRVcGRhdGVTdGF0ZSgpXG5cbiAgICBjb25zdCBvbk9wZW5Qcm9taXNlID0gbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgdGhpcy5sb2cudmVyYm9zZSgnSW9TZXJ2aWNlJywgJ2Nvbm5lY3RSeFNvY2tldCgpIFByb21pc2UoKScpXG5cbiAgICAgIGNvbnN0IGlkID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgIHRoaXMuX3dlYnNvY2tldCA9IG51bGxcbiAgICAgICAgY29uc3QgZSA9IG5ldyBFcnJvcigncnhTb2NrZXQgY29ubmVjdCB0aW1lb3V0IGFmdGVyICdcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICArIE1hdGgucm91bmQodGhpcy5DT05ORUNUX1RJTUVPVVQgLyAxMDAwKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgKVxuICAgICAgICByZWplY3QoZSlcbiAgICAgIH0sIHRoaXMuQ09OTkVDVF9USU1FT1VUKSAvLyB0aW1lb3V0IGZvciBjb25uZWN0IHdlYnNvY2tldFxuXG4gICAgICB0aGlzLl93ZWJzb2NrZXQub25vcGVuID0gKGUpID0+IHtcbiAgICAgICAgdGhpcy5sb2cudmVyYm9zZSgnSW9TZXJ2aWNlJywgJ2Nvbm5lY3RSeFNvY2tldCgpIFByb21pc2UoKSBXZWJTb2NrZXQub25PcGVuKCkgcmVzb2x2ZSgpJylcbiAgICAgICAgdGhpcy5zb2NrZXRVcGRhdGVTdGF0ZSgpXG4gICAgICAgIGNsZWFyVGltZW91dChpZClcbiAgICAgICAgcmVzb2x2ZSgpXG4gICAgICB9XG4gICAgfSlcblxuICAgIC8vIEhhbmRsZSB0aGUgcGF5bG9hZFxuICAgIHRoaXMuX3dlYnNvY2tldC5vbm1lc3NhZ2UgPSB0aGlzLnNvY2tldE9uTWVzc2FnZS5iaW5kKHRoaXMpXG4gICAgLy8gRGVhbCB0aGUgZXZlbnRcbiAgICB0aGlzLl93ZWJzb2NrZXQub25lcnJvciAgID0gdGhpcy5zb2NrZXRPbkVycm9yLmJpbmQodGhpcylcbiAgICB0aGlzLl93ZWJzb2NrZXQub25jbG9zZSAgID0gdGhpcy5zb2NrZXRPbkNsb3NlLmJpbmQodGhpcylcblxuICAgIHJldHVybiBvbk9wZW5Qcm9taXNlXG4gIH1cblxuICBwcml2YXRlIGVuZFBvaW50KCk6IHN0cmluZyB7XG4gICAgY29uc3QgdXJsID0gdGhpcy5FTkRQT0lOVCArIHRoaXMuX3Rva2VuXG4gICAgdGhpcy5sb2cudmVyYm9zZSgnSW9TZXJ2aWNlJywgJ2VuZFBvaW50KCkgPT4gJXMnLCB1cmwpXG4gICAgcmV0dXJuIHVybFxuICB9XG5cbiAgLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuICAgKlxuICAgKiBTdGF0ZSBFdmVudCBMaXN0ZW5lcnNcbiAgICpcbiAgICovXG4gIHByaXZhdGUgc3RhdGVPbk9wZW4oKSB7XG4gICAgdGhpcy5sb2cudmVyYm9zZSgnSW9TZXJ2aWNlJywgJ3N0YXRlT25PcGVuKCknKVxuXG4gICAgdGhpcy5zb2NrZXRTZW5kQnVmZmVyKClcbiAgICB0aGlzLnJwY1VwZGF0ZSgnZnJvbSBzdGF0ZU9uT3BlbigpJylcbiAgfVxuXG4gIC8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcbiAgICpcbiAgICogSW8gUlBDIE1ldGhvZHNcbiAgICpcbiAgICovXG4gIGFzeW5jIHJwY0RpbmcocGF5bG9hZDogYW55KTogUHJvbWlzZTxhbnk+IHtcbiAgICB0aGlzLmxvZy52ZXJib3NlKCdJb1NlcnZpY2UnLCAnZGluZyglcyknLCBwYXlsb2FkKVxuXG4gICAgY29uc3QgZTogSW9FdmVudCA9IHtcbiAgICAgIG5hbWU6ICdkaW5nJyxcbiAgICAgIHBheWxvYWQsXG4gICAgfVxuICAgIHRoaXMuZXZlbnQubmV4dChlKVxuICAgIC8vIFRPRE86IGdldCB0aGUgcmV0dXJuIHZhbHVlXG4gIH1cblxuICBhc3luYyBycGNVcGRhdGUocGF5bG9hZDogYW55KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5ldmVudC5uZXh0KHtcbiAgICAgIG5hbWU6ICAgICAndXBkYXRlJyxcbiAgICAgIHBheWxvYWQsXG4gICAgfSlcbiAgfVxuXG4gIC8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcbiAgICpcbiAgICogU29ja2V0IEFjdGlvbnNcbiAgICpcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgc29ja2V0Q2xvc2UoY29kZT86IG51bWJlciwgcmVhc29uPzogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5sb2cudmVyYm9zZSgnSW9TZXJ2aWNlJywgJ3NvY2tldENsb3NlKCknKVxuXG4gICAgaWYgKCF0aGlzLl93ZWJzb2NrZXQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbm8gd2Vic29ja2V0JylcbiAgICB9XG5cbiAgICB0aGlzLl93ZWJzb2NrZXQuY2xvc2UoY29kZSwgcmVhc29uKVxuICAgIHRoaXMuc29ja2V0VXBkYXRlU3RhdGUoKVxuXG4gICAgY29uc3QgZnV0dXJlID0gbmV3IFByb21pc2UocmVzb2x2ZSA9PiB7XG4gICAgICB0aGlzLnJlYWR5U3RhdGUucGlwZShcbiAgICAgICAgZmlsdGVyKHMgPT4gcyA9PT0gUmVhZHlTdGF0ZS5DTE9TRUQpLFxuICAgICAgKVxuICAgICAgLnN1YnNjcmliZShyZXNvbHZlKVxuICAgIH0pXG4gICAgYXdhaXQgZnV0dXJlXG5cbiAgICByZXR1cm5cbiAgfVxuXG4gIHByaXZhdGUgc29ja2V0U2VuZChpb0V2ZW50OiBJb0V2ZW50KSB7XG4gICAgdGhpcy5sb2cuc2lsbHkoJ0lvU2VydmljZScsICdzb2NrZXRTZW5kKHtuYW1lOiVzLCBwYXlsb2FkOiVzfSknLCBpb0V2ZW50Lm5hbWUsIGlvRXZlbnQucGF5bG9hZClcblxuICAgIGlmICghdGhpcy5fd2Vic29ja2V0KSB7XG4gICAgICB0aGlzLmxvZy5zaWxseSgnSW9TZXJ2aWNlJywgJ3NvY2tldFNlbmQoKSBubyBfd2Vic29ja2V0JylcbiAgICB9XG5cbiAgICBjb25zdCBzdHJFdnQgPSBKU09OLnN0cmluZ2lmeShpb0V2ZW50KVxuICAgIHRoaXMuc2VuZEJ1ZmZlci5wdXNoKHN0ckV2dClcblxuICAgIC8vIFhYWCBjYW4gbW92ZSB0aGlzIHRvIG9uT3Blbj9cbiAgICB0aGlzLnNvY2tldFNlbmRCdWZmZXIoKVxuICB9XG5cbiAgcHJpdmF0ZSBzb2NrZXRTZW5kQnVmZmVyKCk6IHZvaWQge1xuICAgIHRoaXMubG9nLnNpbGx5KCdJb1NlcnZpY2UnLCAnc29ja2V0U2VuZEJ1ZmZlcigpIGxlbmd0aDolcycsIHRoaXMuc2VuZEJ1ZmZlci5sZW5ndGgpXG5cbiAgICBpZiAoIXRoaXMuX3dlYnNvY2tldCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdzb2NrZXRTZW5kQnVmZmVyKCk6IG5vIF93ZWJzb2NrZXQnKVxuICAgIH1cblxuICAgIGlmICh0aGlzLl93ZWJzb2NrZXQucmVhZHlTdGF0ZSAhPT0gV2ViU29ja2V0Lk9QRU4pIHtcbiAgICAgIHRoaXMubG9nLndhcm4oJ0lvU2VydmljZScsICdzb2NrZXRTZW5kQnVmZmVyKCkgcmVhZHlTdGF0ZSBpcyBub3QgT1BFTiwgc2VuZCBqb2IgZGVsYXllZC4nKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgd2hpbGUgKHRoaXMuc2VuZEJ1ZmZlci5sZW5ndGgpIHtcbiAgICAgIGNvbnN0IGJ1ZiA9IHRoaXMuc2VuZEJ1ZmZlci5zaGlmdCgpXG4gICAgICB0aGlzLmxvZy5zaWxseSgnSW9TZXJ2aWNlJywgJ3NvY2tldFNlbmRCdWZmZXIoKSBzZW5kaW5nKCVzKScsIGJ1ZilcbiAgICAgIHRoaXMuX3dlYnNvY2tldC5zZW5kKGJ1ZilcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHNvY2tldFVwZGF0ZVN0YXRlKCkge1xuICAgIHRoaXMubG9nLnZlcmJvc2UoJ0lvU2VydmljZScsICdzb2NrZXRVcGRhdGVTdGF0ZSgpIGlzICVzJyxcbiAgICAgIFJlYWR5U3RhdGVbdGhpcy5fd2Vic29ja2V0Py5yZWFkeVN0YXRlXSxcbiAgICApXG5cbiAgICBpZiAoIXRoaXMuX3dlYnNvY2tldCkge1xuICAgICAgdGhpcy5sb2cuZXJyb3IoJ0lvU2VydmljZScsICdzb2NrZXRVcGRhdGVTdGF0ZSgpIG5vIF93ZWJzb2NrZXQnKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgdGhpcy5fcmVhZHlTdGF0ZS5uZXh0KHRoaXMuX3dlYnNvY2tldC5yZWFkeVN0YXRlKVxuICB9XG5cbiAgLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuICAgKlxuICAgKiBTb2NrZXQgRXZlbnRzIExpc3RlbmVyXG4gICAqXG4gICAqL1xuICBwcml2YXRlIHNvY2tldE9uTWVzc2FnZShtZXNzYWdlOiBNZXNzYWdlRXZlbnQpIHtcbiAgICB0aGlzLmxvZy52ZXJib3NlKCdJb1NlcnZpY2UnLCAnb25NZXNzYWdlKHtkYXRhOiAlc30pJywgbWVzc2FnZS5kYXRhKVxuXG4gICAgY29uc3QgZGF0YSA9IG1lc3NhZ2UuZGF0YSAvLyBXZWJTb2NrZXQgZGF0YVxuXG4gICAgY29uc3QgaW9FdmVudDogSW9FdmVudCA9IHtcbiAgICAgIG5hbWU6ICAgICAncmF3JyxcbiAgICAgIHBheWxvYWQ6ICBkYXRhLFxuICAgIH0gLy8gdGhpcyBpcyBkZWZhdWx0IGlvIGV2ZW50IGZvciB1bmtub3duIGZvcm1hdCBtZXNzYWdlXG5cbiAgICB0cnkge1xuICAgICAgY29uc3Qgb2JqID0gSlNPTi5wYXJzZShkYXRhKVxuICAgICAgaW9FdmVudC5uYW1lID0gb2JqLm5hbWVcbiAgICAgIGlvRXZlbnQucGF5bG9hZCA9IG9iai5wYXlsb2FkXG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgdGhpcy5sb2cud2FybignSW9TZXJ2aWNlJywgJ29uTWVzc2FnZSBwYXJzZSBtZXNzYWdlIGZhaWwuIHNhdmUgYXMgUkFXJylcbiAgICB9XG5cbiAgICB0aGlzLm10T2JzZXJ2ZXIubmV4dChpb0V2ZW50KVxuICB9XG5cbiAgcHJpdmF0ZSBzb2NrZXRPbkVycm9yKGV2ZW50OiBFdmVudCkge1xuICAgIHRoaXMubG9nLnNpbGx5KCdJb1NlcnZpY2UnLCAnc29ja2V0T25FcnJvciglcyknLCBldmVudClcbiAgICAvLyB0aGlzLl93ZWJzb2NrZXQgPSBudWxsXG4gIH1cblxuICAvKipcbiAgICogaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL0Nsb3NlRXZlbnRcbiAgICogY29kZTogMTAwNlx0Q0xPU0VfQUJOT1JNQUxcbiAgICogIC0gUmVzZXJ2ZWQuIFVzZWQgdG8gaW5kaWNhdGUgdGhhdCBhIGNvbm5lY3Rpb24gd2FzIGNsb3NlZCBhYm5vcm1hbGx5XG4gICAqICAgICh0aGF0IGlzLCB3aXRoIG5vIGNsb3NlIGZyYW1lIGJlaW5nIHNlbnQpIHdoZW4gYSBzdGF0dXMgY29kZSBpcyBleHBlY3RlZC5cbiAgICovXG4gIHByaXZhdGUgc29ja2V0T25DbG9zZShjbG9zZUV2ZW50OiBDbG9zZUV2ZW50KSB7XG4gICAgdGhpcy5sb2cudmVyYm9zZSgnSW9TZXJ2aWNlJywgJ3NvY2tldE9uQ2xvc2Uoe2NvZGU6JXMsIHJlYXNvbjolcywgcmV0dXJuVmFsdWU6JXN9KScsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xvc2VFdmVudC5jb2RlLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsb3NlRXZlbnQucmVhc29uLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsb3NlRXZlbnQucmV0dXJuVmFsdWUsXG4gICAgICAgICAgICAgICAgICAgIClcbiAgICB0aGlzLnNvY2tldFVwZGF0ZVN0YXRlKClcbiAgICAvKipcbiAgICAgKiByZWNvbm5lY3QgaW5zaWRlIG9uQ2xvc2VcbiAgICAgKi9cbiAgICBpZiAodGhpcy5hdXRvUmVjb25uZWN0KSB7XG4gICAgICB0aGlzLnN0YXRlLm9uKCdwZW5kaW5nJylcbiAgICAgIHNldFRpbWVvdXQoYXN5bmMgKCkgPT4ge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGF3YWl0IHRoaXMuY29ubmVjdFJ4U29ja2V0KClcbiAgICAgICAgICB0aGlzLnN0YXRlLm9uKHRydWUpXG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICB0aGlzLmxvZy53YXJuKCdJb1NlcnZpY2UnLCAnc29ja2V0T25DbG9zZSgpIGF1dG9SZWNvbm5lY3QoKSBleGNlcHRpb246ICVzJywgZSlcbiAgICAgICAgICB0aGlzLnN0YXRlLm9mZih0cnVlKVxuICAgICAgICB9XG4gICAgICB9LCAxMDAwKVxuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnN0YXRlLm9mZih0cnVlKVxuICAgIH1cbiAgICB0aGlzLl93ZWJzb2NrZXQgPSBudWxsXG5cbiAgICBpZiAoIWNsb3NlRXZlbnQud2FzQ2xlYW4pIHtcbiAgICAgIHRoaXMubG9nLndhcm4oJ0lvU2VydmljZScsICdzb2NrZXRPbkNsb3NlKCkgZXZlbnQud2FzQ2xlYW4gRkFMU0UnKVxuICAgICAgLy8gVE9ETyBlbWl0IGVycm9yXG4gICAgfVxuICB9XG59XG4iXX0=