@hivegpt/hiveai-angular 0.0.585 → 0.0.587
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/bundles/hivegpt-hiveai-angular.umd.js +733 -260
- package/bundles/hivegpt-hiveai-angular.umd.js.map +1 -1
- package/bundles/hivegpt-hiveai-angular.umd.min.js +1 -1
- package/bundles/hivegpt-hiveai-angular.umd.min.js.map +1 -1
- package/esm2015/hivegpt-hiveai-angular.js +6 -4
- package/esm2015/lib/components/voice-agent/components/voice-agent-modal/voice-agent-modal.component.js +24 -17
- package/esm2015/lib/components/voice-agent/services/audio-analyzer.service.js +3 -3
- package/esm2015/lib/components/voice-agent/services/daily-voice-client.service.js +326 -0
- package/esm2015/lib/components/voice-agent/services/voice-agent.service.js +187 -189
- package/esm2015/lib/components/voice-agent/services/websocket-voice-client.service.js +95 -0
- package/esm2015/lib/components/voice-agent/voice-agent.module.js +7 -3
- package/fesm2015/hivegpt-hiveai-angular.js +624 -207
- package/fesm2015/hivegpt-hiveai-angular.js.map +1 -1
- package/hivegpt-hiveai-angular.d.ts +5 -3
- package/hivegpt-hiveai-angular.d.ts.map +1 -1
- package/hivegpt-hiveai-angular.metadata.json +1 -1
- package/lib/components/voice-agent/components/voice-agent-modal/voice-agent-modal.component.d.ts +4 -7
- package/lib/components/voice-agent/components/voice-agent-modal/voice-agent-modal.component.d.ts.map +1 -1
- package/lib/components/voice-agent/services/audio-analyzer.service.d.ts +2 -2
- package/lib/components/voice-agent/services/daily-voice-client.service.d.ts +70 -0
- package/lib/components/voice-agent/services/daily-voice-client.service.d.ts.map +1 -0
- package/lib/components/voice-agent/services/voice-agent.service.d.ts +19 -23
- package/lib/components/voice-agent/services/voice-agent.service.d.ts.map +1 -1
- package/lib/components/voice-agent/services/websocket-voice-client.service.d.ts +49 -0
- package/lib/components/voice-agent/services/websocket-voice-client.service.d.ts.map +1 -0
- package/lib/components/voice-agent/voice-agent.module.d.ts +2 -2
- package/lib/components/voice-agent/voice-agent.module.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { __awaiter } from "tslib";
|
|
2
|
+
import { Injectable, NgZone } from '@angular/core';
|
|
3
|
+
import { BehaviorSubject } from 'rxjs';
|
|
4
|
+
import Daily from '@daily-co/daily-js';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
/**
|
|
7
|
+
* Daily.js WebRTC client for voice agent audio.
|
|
8
|
+
* Responsibilities:
|
|
9
|
+
* - Create and manage Daily CallObject
|
|
10
|
+
* - Join Daily room using room_url
|
|
11
|
+
* - Handle mic capture + speaker playback
|
|
12
|
+
* - Bot speaking detection via AnalyserNode on remote track (instant)
|
|
13
|
+
* - User speaking detection via active-speaker-change
|
|
14
|
+
* - Expose speaking$ (bot speaking), userSpeaking$ (user speaking), micMuted$
|
|
15
|
+
* - Expose localStream$ for waveform visualization (AudioAnalyzerService)
|
|
16
|
+
*/
|
|
17
|
+
export class DailyVoiceClientService {
|
|
18
|
+
constructor(ngZone) {
|
|
19
|
+
this.ngZone = ngZone;
|
|
20
|
+
this.callObject = null;
|
|
21
|
+
this.localStream = null;
|
|
22
|
+
this.localSessionId = null;
|
|
23
|
+
/** Explicit playback of remote (bot) audio; required in some browsers. */
|
|
24
|
+
this.remoteAudioElement = null;
|
|
25
|
+
/** AnalyserNode-based remote audio monitor for instant bot speaking detection. */
|
|
26
|
+
this.remoteAudioContext = null;
|
|
27
|
+
this.remoteSpeakingRAF = null;
|
|
28
|
+
this.speakingSubject = new BehaviorSubject(false);
|
|
29
|
+
this.userSpeakingSubject = new BehaviorSubject(false);
|
|
30
|
+
this.micMutedSubject = new BehaviorSubject(false);
|
|
31
|
+
this.localStreamSubject = new BehaviorSubject(null);
|
|
32
|
+
this.firstRemoteAudioFrameSubject = new BehaviorSubject(false);
|
|
33
|
+
/** True when bot (remote participant) is the active speaker. */
|
|
34
|
+
this.speaking$ = this.speakingSubject.asObservable();
|
|
35
|
+
/** True when user (local participant) is the active speaker. */
|
|
36
|
+
this.userSpeaking$ = this.userSpeakingSubject.asObservable();
|
|
37
|
+
/** True when mic is muted. */
|
|
38
|
+
this.micMuted$ = this.micMutedSubject.asObservable();
|
|
39
|
+
/** Emits local mic stream for waveform visualization. */
|
|
40
|
+
this.localStream$ = this.localStreamSubject.asObservable();
|
|
41
|
+
/** Emits true once when first remote audio frame starts playing. */
|
|
42
|
+
this.firstRemoteAudioFrame$ = this.firstRemoteAudioFrameSubject.asObservable();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Prompt for microphone access up front so callers can handle permission
|
|
46
|
+
* denial before creating backend room/session resources.
|
|
47
|
+
*/
|
|
48
|
+
ensureMicrophoneAccess() {
|
|
49
|
+
var _a;
|
|
50
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
51
|
+
if (!((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia)) {
|
|
52
|
+
throw new Error('Microphone API unavailable');
|
|
53
|
+
}
|
|
54
|
+
const stream = yield navigator.mediaDevices.getUserMedia({ audio: true });
|
|
55
|
+
stream.getTracks().forEach((t) => t.stop());
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Connect to Daily room. Acquires mic first for waveform, then joins with audio.
|
|
60
|
+
* @param roomUrl Daily room URL (from room_created)
|
|
61
|
+
* @param token Optional meeting token
|
|
62
|
+
*/
|
|
63
|
+
connect(roomUrl, token) {
|
|
64
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
if (this.callObject) {
|
|
66
|
+
yield this.disconnect();
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
// Get mic stream for both Daily and waveform (single capture)
|
|
70
|
+
const stream = yield navigator.mediaDevices.getUserMedia({ audio: true });
|
|
71
|
+
const audioTrack = stream.getAudioTracks()[0];
|
|
72
|
+
if (!audioTrack) {
|
|
73
|
+
stream.getTracks().forEach((t) => t.stop());
|
|
74
|
+
throw new Error('No audio track');
|
|
75
|
+
}
|
|
76
|
+
this.localStream = stream;
|
|
77
|
+
this.localStreamSubject.next(stream);
|
|
78
|
+
// Create audio-only call object
|
|
79
|
+
// videoSource: false = no camera, audioSource = our mic track
|
|
80
|
+
const callObject = Daily.createCallObject({
|
|
81
|
+
videoSource: false,
|
|
82
|
+
audioSource: audioTrack,
|
|
83
|
+
});
|
|
84
|
+
this.callObject = callObject;
|
|
85
|
+
this.setupEventHandlers(callObject);
|
|
86
|
+
// Join room; Daily handles playback of remote (bot) audio automatically.
|
|
87
|
+
// Only pass token when it's a non-empty string (Daily rejects undefined/non-string).
|
|
88
|
+
const joinOptions = { url: roomUrl };
|
|
89
|
+
if (typeof token === 'string' && token.trim() !== '') {
|
|
90
|
+
joinOptions.token = token;
|
|
91
|
+
}
|
|
92
|
+
yield callObject.join(joinOptions);
|
|
93
|
+
console.log(`[VoiceDebug] Room connected (Daily join complete) — ${new Date().toISOString()}`);
|
|
94
|
+
const participants = callObject.participants();
|
|
95
|
+
if (participants === null || participants === void 0 ? void 0 : participants.local) {
|
|
96
|
+
this.localSessionId = participants.local.session_id;
|
|
97
|
+
}
|
|
98
|
+
// Start with mic muted; VoiceAgentService auto-unmutes after first remote audio frame.
|
|
99
|
+
callObject.setLocalAudio(false);
|
|
100
|
+
this.micMutedSubject.next(true);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
this.cleanup();
|
|
104
|
+
throw err;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
setupEventHandlers(call) {
|
|
109
|
+
// active-speaker-change: used ONLY for user speaking detection.
|
|
110
|
+
// Bot speaking is detected by our own AnalyserNode (instant, no debounce).
|
|
111
|
+
call.on('active-speaker-change', (event) => {
|
|
112
|
+
this.ngZone.run(() => {
|
|
113
|
+
var _a;
|
|
114
|
+
const peerId = (_a = event === null || event === void 0 ? void 0 : event.activeSpeaker) === null || _a === void 0 ? void 0 : _a.peerId;
|
|
115
|
+
if (!peerId || !this.localSessionId) {
|
|
116
|
+
this.userSpeakingSubject.next(false);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const isLocal = peerId === this.localSessionId;
|
|
120
|
+
this.userSpeakingSubject.next(isLocal);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
// track-started / track-stopped: set up remote audio playback + AnalyserNode monitor.
|
|
124
|
+
call.on('track-started', (event) => {
|
|
125
|
+
this.ngZone.run(() => {
|
|
126
|
+
var _a, _b, _c, _d;
|
|
127
|
+
const p = event === null || event === void 0 ? void 0 : event.participant;
|
|
128
|
+
const type = (_a = event === null || event === void 0 ? void 0 : event.type) !== null && _a !== void 0 ? _a : (_b = event === null || event === void 0 ? void 0 : event.track) === null || _b === void 0 ? void 0 : _b.kind;
|
|
129
|
+
const track = event === null || event === void 0 ? void 0 : event.track;
|
|
130
|
+
if (p && !p.local && type === 'audio') {
|
|
131
|
+
console.log(`[VoiceDebug] Got audio track from backend (track-started) — readyState=${track === null || track === void 0 ? void 0 : track.readyState}, muted=${track === null || track === void 0 ? void 0 : track.muted} — ${new Date().toISOString()}`);
|
|
132
|
+
const audioTrack = track !== null && track !== void 0 ? track : (_d = (_c = p.tracks) === null || _c === void 0 ? void 0 : _c.audio) === null || _d === void 0 ? void 0 : _d.track;
|
|
133
|
+
if (audioTrack && typeof audioTrack === 'object') {
|
|
134
|
+
this.playRemoteTrack(audioTrack);
|
|
135
|
+
this.monitorRemoteAudio(audioTrack);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
call.on('track-stopped', (event) => {
|
|
141
|
+
this.ngZone.run(() => {
|
|
142
|
+
var _a, _b;
|
|
143
|
+
const p = event === null || event === void 0 ? void 0 : event.participant;
|
|
144
|
+
const type = (_a = event === null || event === void 0 ? void 0 : event.type) !== null && _a !== void 0 ? _a : (_b = event === null || event === void 0 ? void 0 : event.track) === null || _b === void 0 ? void 0 : _b.kind;
|
|
145
|
+
if (p && !p.local && type === 'audio') {
|
|
146
|
+
this.stopRemoteAudioMonitor();
|
|
147
|
+
this.stopRemoteAudio();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
call.on('left-meeting', () => {
|
|
152
|
+
this.ngZone.run(() => this.cleanup());
|
|
153
|
+
});
|
|
154
|
+
call.on('error', (event) => {
|
|
155
|
+
this.ngZone.run(() => {
|
|
156
|
+
var _a;
|
|
157
|
+
console.error('DailyVoiceClient: Daily error', (_a = event === null || event === void 0 ? void 0 : event.errorMsg) !== null && _a !== void 0 ? _a : event);
|
|
158
|
+
this.cleanup();
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Play remote (bot) audio track via a dedicated audio element.
|
|
164
|
+
* Required in many browsers where Daily's internal playback does not output to speakers.
|
|
165
|
+
*/
|
|
166
|
+
playRemoteTrack(track) {
|
|
167
|
+
this.stopRemoteAudio();
|
|
168
|
+
try {
|
|
169
|
+
console.log(`[VoiceDebug] playRemoteTrack called — track.readyState=${track.readyState}, track.muted=${track.muted} — ${new Date().toISOString()}`);
|
|
170
|
+
track.onunmute = () => {
|
|
171
|
+
console.log(`[VoiceDebug] Remote audio track UNMUTED (audio data arriving) — ${new Date().toISOString()}`);
|
|
172
|
+
};
|
|
173
|
+
const stream = new MediaStream([track]);
|
|
174
|
+
const audio = new Audio();
|
|
175
|
+
audio.autoplay = true;
|
|
176
|
+
audio.srcObject = stream;
|
|
177
|
+
this.remoteAudioElement = audio;
|
|
178
|
+
audio.onplaying = () => {
|
|
179
|
+
console.log(`[VoiceDebug] Audio element PLAYING (browser started playback) — ${new Date().toISOString()}`);
|
|
180
|
+
};
|
|
181
|
+
let firstTimeUpdate = true;
|
|
182
|
+
audio.ontimeupdate = () => {
|
|
183
|
+
if (firstTimeUpdate) {
|
|
184
|
+
firstTimeUpdate = false;
|
|
185
|
+
console.log(`[VoiceDebug] Audio element first TIMEUPDATE (actual audio output) — ${new Date().toISOString()}`);
|
|
186
|
+
this.firstRemoteAudioFrameSubject.next(true);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
const p = audio.play();
|
|
190
|
+
if (p && typeof p.then === 'function') {
|
|
191
|
+
p.then(() => {
|
|
192
|
+
console.log(`[VoiceDebug] audio.play() resolved — ${new Date().toISOString()}`);
|
|
193
|
+
this.firstRemoteAudioFrameSubject.next(true);
|
|
194
|
+
}).catch((err) => {
|
|
195
|
+
console.warn('DailyVoiceClient: remote audio play failed (may need user gesture)', err);
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
console.warn('DailyVoiceClient: failed to create remote audio element', err);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Monitor remote audio track energy via AnalyserNode.
|
|
205
|
+
* Polls at ~60fps and flips speakingSubject based on actual audio energy.
|
|
206
|
+
*/
|
|
207
|
+
monitorRemoteAudio(track) {
|
|
208
|
+
this.stopRemoteAudioMonitor();
|
|
209
|
+
try {
|
|
210
|
+
const ctx = new AudioContext();
|
|
211
|
+
const source = ctx.createMediaStreamSource(new MediaStream([track]));
|
|
212
|
+
const analyser = ctx.createAnalyser();
|
|
213
|
+
analyser.fftSize = 256;
|
|
214
|
+
source.connect(analyser);
|
|
215
|
+
this.remoteAudioContext = ctx;
|
|
216
|
+
const dataArray = new Uint8Array(analyser.frequencyBinCount);
|
|
217
|
+
const THRESHOLD = 5;
|
|
218
|
+
const SILENCE_MS = 1500;
|
|
219
|
+
let lastSoundTime = 0;
|
|
220
|
+
let isSpeaking = false;
|
|
221
|
+
const poll = () => {
|
|
222
|
+
if (!this.remoteAudioContext)
|
|
223
|
+
return;
|
|
224
|
+
analyser.getByteFrequencyData(dataArray);
|
|
225
|
+
let sum = 0;
|
|
226
|
+
for (let i = 0; i < dataArray.length; i++) {
|
|
227
|
+
sum += dataArray[i];
|
|
228
|
+
}
|
|
229
|
+
const avg = sum / dataArray.length;
|
|
230
|
+
const now = Date.now();
|
|
231
|
+
if (avg > THRESHOLD) {
|
|
232
|
+
lastSoundTime = now;
|
|
233
|
+
if (!isSpeaking) {
|
|
234
|
+
isSpeaking = true;
|
|
235
|
+
console.log(`[VoiceDebug] Bot audio energy detected (speaking=true) — avg=${avg.toFixed(1)} — ${new Date().toISOString()}`);
|
|
236
|
+
this.ngZone.run(() => {
|
|
237
|
+
this.userSpeakingSubject.next(false);
|
|
238
|
+
this.speakingSubject.next(true);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
else if (isSpeaking && now - lastSoundTime > SILENCE_MS) {
|
|
243
|
+
isSpeaking = false;
|
|
244
|
+
console.log(`[VoiceDebug] Bot audio silence detected (speaking=false) — ${new Date().toISOString()}`);
|
|
245
|
+
this.ngZone.run(() => this.speakingSubject.next(false));
|
|
246
|
+
}
|
|
247
|
+
this.remoteSpeakingRAF = requestAnimationFrame(poll);
|
|
248
|
+
};
|
|
249
|
+
this.remoteSpeakingRAF = requestAnimationFrame(poll);
|
|
250
|
+
}
|
|
251
|
+
catch (err) {
|
|
252
|
+
console.warn('DailyVoiceClient: failed to create remote audio monitor', err);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
stopRemoteAudioMonitor() {
|
|
256
|
+
if (this.remoteSpeakingRAF) {
|
|
257
|
+
cancelAnimationFrame(this.remoteSpeakingRAF);
|
|
258
|
+
this.remoteSpeakingRAF = null;
|
|
259
|
+
}
|
|
260
|
+
if (this.remoteAudioContext) {
|
|
261
|
+
this.remoteAudioContext.close().catch(() => { });
|
|
262
|
+
this.remoteAudioContext = null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
stopRemoteAudio() {
|
|
266
|
+
if (this.remoteAudioElement) {
|
|
267
|
+
try {
|
|
268
|
+
this.remoteAudioElement.pause();
|
|
269
|
+
this.remoteAudioElement.srcObject = null;
|
|
270
|
+
}
|
|
271
|
+
catch (_) { }
|
|
272
|
+
this.remoteAudioElement = null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/** Set mic muted state. */
|
|
276
|
+
setMuted(muted) {
|
|
277
|
+
if (!this.callObject)
|
|
278
|
+
return;
|
|
279
|
+
this.callObject.setLocalAudio(!muted);
|
|
280
|
+
this.micMutedSubject.next(muted);
|
|
281
|
+
}
|
|
282
|
+
/** Disconnect and cleanup. */
|
|
283
|
+
disconnect() {
|
|
284
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
285
|
+
if (!this.callObject) {
|
|
286
|
+
this.cleanup();
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
try {
|
|
290
|
+
yield this.callObject.leave();
|
|
291
|
+
}
|
|
292
|
+
catch (e) {
|
|
293
|
+
// ignore
|
|
294
|
+
}
|
|
295
|
+
this.cleanup();
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
cleanup() {
|
|
299
|
+
this.stopRemoteAudioMonitor();
|
|
300
|
+
this.stopRemoteAudio();
|
|
301
|
+
if (this.callObject) {
|
|
302
|
+
this.callObject.destroy().catch(() => { });
|
|
303
|
+
this.callObject = null;
|
|
304
|
+
}
|
|
305
|
+
if (this.localStream) {
|
|
306
|
+
this.localStream.getTracks().forEach((t) => t.stop());
|
|
307
|
+
this.localStream = null;
|
|
308
|
+
}
|
|
309
|
+
this.localSessionId = null;
|
|
310
|
+
this.speakingSubject.next(false);
|
|
311
|
+
this.userSpeakingSubject.next(false);
|
|
312
|
+
this.localStreamSubject.next(null);
|
|
313
|
+
this.firstRemoteAudioFrameSubject.next(false);
|
|
314
|
+
// Keep last micMuted state; will reset on next connect
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
DailyVoiceClientService.ɵprov = i0.ɵɵdefineInjectable({ factory: function DailyVoiceClientService_Factory() { return new DailyVoiceClientService(i0.ɵɵinject(i0.NgZone)); }, token: DailyVoiceClientService, providedIn: "root" });
|
|
318
|
+
DailyVoiceClientService.decorators = [
|
|
319
|
+
{ type: Injectable, args: [{
|
|
320
|
+
providedIn: 'root',
|
|
321
|
+
},] }
|
|
322
|
+
];
|
|
323
|
+
DailyVoiceClientService.ctorParameters = () => [
|
|
324
|
+
{ type: NgZone }
|
|
325
|
+
];
|
|
326
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGFpbHktdm9pY2UtY2xpZW50LnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiL1VzZXJzL3JvaGl0dGhha3VyL2hpdmUtZ3B0L0hpdmVBSS1QYWNrYWdlcy9Bbmd1bGFyL3Byb2plY3RzL2hpdmVncHQvZXZlbnRzZ3B0LWFuZ3VsYXIvc3JjLyIsInNvdXJjZXMiOlsibGliL2NvbXBvbmVudHMvdm9pY2UtYWdlbnQvc2VydmljZXMvZGFpbHktdm9pY2UtY2xpZW50LnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ25ELE9BQU8sRUFBRSxlQUFlLEVBQWMsTUFBTSxNQUFNLENBQUM7QUFDbkQsT0FBTyxLQUFLLE1BQU0sb0JBQW9CLENBQUM7O0FBR3ZDOzs7Ozs7Ozs7O0dBVUc7QUFJSCxNQUFNLE9BQU8sdUJBQXVCO0lBa0NsQyxZQUFvQixNQUFjO1FBQWQsV0FBTSxHQUFOLE1BQU0sQ0FBUTtRQWpDMUIsZUFBVSxHQUFxQixJQUFJLENBQUM7UUFDcEMsZ0JBQVcsR0FBdUIsSUFBSSxDQUFDO1FBQ3ZDLG1CQUFjLEdBQWtCLElBQUksQ0FBQztRQUM3QywwRUFBMEU7UUFDbEUsdUJBQWtCLEdBQTRCLElBQUksQ0FBQztRQUUzRCxrRkFBa0Y7UUFDMUUsdUJBQWtCLEdBQXdCLElBQUksQ0FBQztRQUMvQyxzQkFBaUIsR0FBa0IsSUFBSSxDQUFDO1FBRXhDLG9CQUFlLEdBQUcsSUFBSSxlQUFlLENBQVUsS0FBSyxDQUFDLENBQUM7UUFDdEQsd0JBQW1CLEdBQUcsSUFBSSxlQUFlLENBQVUsS0FBSyxDQUFDLENBQUM7UUFDMUQsb0JBQWUsR0FBRyxJQUFJLGVBQWUsQ0FBVSxLQUFLLENBQUMsQ0FBQztRQUN0RCx1QkFBa0IsR0FBRyxJQUFJLGVBQWUsQ0FBcUIsSUFBSSxDQUFDLENBQUM7UUFDbkUsaUNBQTRCLEdBQUcsSUFBSSxlQUFlLENBQVUsS0FBSyxD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QUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7WUFDdEQsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUM7U0FDekI7UUFDRCxJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksQ0FBQztRQUMzQixJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNqQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3JDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDbkMsSUFBSSxDQUFDLDRCQUE0QixDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUM5Qyx1REFBdUQ7SUFDekQsQ0FBQzs7OztZQW5VRixVQUFVLFNBQUM7Z0JBQ1YsVUFBVSxFQUFFLE1BQU07YUFDbkI7OztZQWxCb0IsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUsIE5nWm9uZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBPYnNlcnZhYmxlIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgRGFpbHkgZnJvbSAnQGRhaWx5LWNvL2RhaWx5LWpzJztcbmltcG9ydCB0eXBlIHsgRGFpbHlDYWxsLCBEYWlseVBhcnRpY2lwYW50IH0gZnJvbSAnQGRhaWx5LWNvL2RhaWx5LWpzJztcblxuLyoqXG4gKiBEYWlseS5qcyBXZWJSVEMgY2xpZW50IGZvciB2b2ljZSBhZ2VudCBhdWRpby5cbiAqIFJlc3BvbnNpYmlsaXRpZXM6XG4gKiAtIENyZWF0ZSBhbmQgbWFuYWdlIERhaWx5IENhbGxPYmplY3RcbiAqIC0gSm9pbiBEYWlseSByb29tIHVzaW5nIHJvb21fdXJsXG4gKiAtIEhhbmRsZSBtaWMgY2FwdHVyZSArIHNwZWFrZXIgcGxheWJhY2tcbiAqIC0gQm90IHNwZWFraW5nIGRldGVjdGlvbiB2aWEgQW5hbHlzZXJOb2RlIG9uIHJlbW90ZSB0cmFjayAoaW5zdGFudClcbiAqIC0gVXNlciBzcGVha2luZyBkZXRlY3Rpb24gdmlhIGFjdGl2ZS1zcGVha2VyLWNoYW5nZVxuICogLSBFeHBvc2Ugc3BlYWtpbmckIChib3Qgc3BlYWtpbmcpLCB1c2VyU3BlYWtpbmckICh1c2VyIHNwZWFraW5nKSwgbWljTXV0ZWQkXG4gKiAtIEV4cG9zZSBsb2NhbFN0cmVhbSQgZm9yIHdhdmVmb3JtIHZpc3VhbGl6YXRpb24gKEF1ZGlvQW5hbHl6ZXJTZXJ2aWNlKVxuICovXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290Jyxcbn0pXG5leHBvcnQgY2xhc3MgRGFpbHlWb2ljZUNsaWVudFNlcnZpY2Uge1xuICBwcml2YXRlIGNhbGxPYmplY3Q6IERhaWx5Q2FsbCB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGxvY2FsU3RyZWFtOiBNZWRpYVN0cmVhbSB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGxvY2FsU2Vzc2lvbklkOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgLyoqIEV4cGxpY2l0IHBsYXliYWNrIG9mIHJlbW90ZSAoYm90KSBhdWRpbzsgcmVxdWlyZWQgaW4gc29tZSBicm93c2Vycy4gKi9cbiAgcHJpdmF0ZSByZW1vdGVBdWRpb0VsZW1lbnQ6IEhUTUxBdWRpb0VsZW1lbnQgfCBudWxsID0gbnVsbDtcblxuICAvKiogQW5hbHlzZXJOb2RlLWJhc2VkIHJlbW90ZSBhdWRpbyBtb25pdG9yIGZvciBpbnN0YW50IGJvdCBzcGVha2luZyBkZXRlY3Rpb24uICovXG4gIHByaXZhdGUgcmVtb3RlQXVkaW9Db250ZXh0OiBBdWRpb0NvbnRleHQgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSByZW1vdGVTcGVha2luZ1JBRjogbnVtYmVyIHwgbnVsbCA9IG51bGw7XG5cbiAgcHJpdmF0ZSBzcGVha2luZ1N1YmplY3QgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KGZhbHNlKTtcbiAgcHJpdmF0ZSB1c2VyU3BlYWtpbmdTdWJqZWN0ID0gbmV3IEJlaGF2aW9yU3ViamVjdDxib29sZWFuPihmYWxzZSk7XG4gIHByaXZhdGUgbWljTXV0ZWRTdWJqZWN0ID0gbmV3IEJlaGF2aW9yU3ViamVjdDxib29sZWFuPihmYWxzZSk7XG4gIHByaXZhdGUgbG9jYWxTdHJlYW1TdWJqZWN0ID0gbmV3IEJlaGF2aW9yU3ViamVjdDxNZWRpYVN0cmVhbSB8IG51bGw+KG51bGwpO1xuICBwcml2YXRlIGZpcnN0UmVtb3RlQXVkaW9GcmFtZVN1YmplY3QgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KGZhbHNlKTtcblxuICAvKiogVHJ1ZSB3aGVuIGJvdCAocmVtb3RlIHBhcnRpY2lwYW50KSBpcyB0aGUgYWN0aXZlIHNwZWFrZXIuICovXG4gIHNwZWFraW5nJDogT2JzZXJ2YWJsZTxib29sZWFuPiA9IHRoaXMuc3BlYWtpbmdTdWJqZWN0LmFzT2JzZXJ2YWJsZSgpO1xuXG4gIC8qKiBUcnVlIHdoZW4gdXNlciAobG9jYWwgcGFydGljaXBhbnQpIGlzIHRoZSBhY3RpdmUgc3BlYWtlci4gKi9cbiAgdXNlclNwZWFraW5nJDogT2JzZXJ2YWJsZTxib29sZWFuPiA9IHRoaXMudXNlclNwZWFraW5nU3ViamVjdC5hc09ic2VydmFibGUoKTtcblxuICAvKiogVHJ1ZSB3aGVuIG1pYyBpcyBtdXRlZC4gKi9cbiAgbWljTXV0ZWQkOiBPYnNlcnZhYmxlPGJvb2xlYW4+ID0gdGhpcy5taWNNdXRlZFN1YmplY3QuYXNPYnNlcnZhYmxlKCk7XG5cbiAgLyoqIEVtaXRzIGxvY2FsIG1pYyBzdHJlYW0gZm9yIHdhdmVmb3JtIHZpc3VhbGl6YXRpb24uICovXG4gIGxvY2FsU3RyZWFtJDogT2JzZXJ2YWJsZTxNZWRpYVN0cmVhbSB8IG51bGw+ID1cbiAgICB0aGlzLmxvY2FsU3RyZWFtU3ViamVjdC5hc09ic2VydmFibGUoKTtcblxuICAvKiogRW1pdHMgdHJ1ZSBvbmNlIHdoZW4gZmlyc3QgcmVtb3RlIGF1ZGlvIGZyYW1lIHN0YXJ0cyBwbGF5aW5nLiAqL1xuICBmaXJzdFJlbW90ZUF1ZGlvRnJhbWUkOiBPYnNlcnZhYmxlPGJvb2xlYW4+ID1cbiAgICB0aGlzLmZpcnN0UmVtb3RlQXVkaW9GcmFtZVN1YmplY3QuYXNPYnNlcnZhYmxlKCk7XG5cbiAgY29uc3RydWN0b3IocHJpdmF0ZSBuZ1pvbmU6IE5nWm9uZSkge31cblxuICAvKipcbiAgICogUHJvbXB0IGZvciBtaWNyb3Bob25lIGFjY2VzcyB1cCBmcm9udCBzbyBjYWxsZXJzIGNhbiBoYW5kbGUgcGVybWlzc2lvblxuICAgKiBkZW5pYWwgYmVmb3JlIGNyZWF0aW5nIGJhY2tlbmQgcm9vbS9zZXNzaW9uIHJlc291cmNlcy5cbiAgICovXG4gIGFzeW5jIGVuc3VyZU1pY3JvcGhvbmVBY2Nlc3MoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKCFuYXZpZ2F0b3I/Lm1lZGlhRGV2aWNlcz8uZ2V0VXNlck1lZGlhKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ01pY3JvcGhvbmUgQVBJIHVuYXZhaWxhYmxlJyk7XG4gICAgfVxuICAgIGNvbnN0IHN0cmVhbSA9IGF3YWl0IG5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0VXNlck1lZGlhKHsgYXVkaW86IHRydWUgfSk7XG4gICAgc3RyZWFtLmdldFRyYWNrcygpLmZvckVhY2goKHQpID0+IHQuc3RvcCgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb25uZWN0IHRvIERhaWx5IHJvb20uIEFjcXVpcmVzIG1pYyBmaXJzdCBmb3Igd2F2ZWZvcm0sIHRoZW4gam9pbnMgd2l0aCBhdWRpby5cbiAgICogQHBhcmFtIHJvb21VcmwgRGFpbHkgcm9vbSBVUkwgKGZyb20gcm9vbV9jcmVhdGVkKVxuICAgKiBAcGFyYW0gdG9rZW4gT3B0aW9uYWwgbWVldGluZyB0b2tlblxuICAgKi9cbiAgYXN5bmMgY29ubmVjdChyb29tVXJsOiBzdHJpbmcsIHRva2VuPzogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHRoaXMuY2FsbE9iamVjdCkge1xuICAgICAgYXdhaXQgdGhpcy5kaXNjb25uZWN0KCk7XG4gICAgfVxuXG4gICAgdHJ5IHtcbiAgICAgIC8vIEdldCBtaWMgc3RyZWFtIGZvciBib3RoIERhaWx5IGFuZCB3YXZlZm9ybSAoc2luZ2xlIGNhcHR1cmUpXG4gICAgICBjb25zdCBzdHJlYW0gPSBhd2FpdCBuYXZpZ2F0b3IubWVkaWFEZXZpY2VzLmdldFVzZXJNZWRpYSh7IGF1ZGlvOiB0cnVlIH0pO1xuICAgICAgY29uc3QgYXVkaW9UcmFjayA9IHN0cmVhbS5nZXRBdWRpb1RyYWNrcygpWzBdO1xuICAgICAgaWYgKCFhdWRpb1RyYWNrKSB7XG4gICAgICAgIHN0cmVhbS5nZXRUcmFja3MoKS5mb3JFYWNoKCh0KSA9PiB0LnN0b3AoKSk7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignTm8gYXVkaW8gdHJhY2snKTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5sb2NhbFN0cmVhbSA9IHN0cmVhbTtcbiAgICAgIHRoaXMubG9jYWxTdHJlYW1TdWJqZWN0Lm5leHQoc3RyZWFtKTtcblxuICAgICAgLy8gQ3JlYXRlIGF1ZGlvLW9ubHkgY2FsbCBvYmplY3RcbiAgICAgIC8vIHZpZGVvU291cmNlOiBmYWxzZSA9IG5vIGNhbWVyYSwgYXVkaW9Tb3VyY2UgPSBvdXIgbWljIHRyYWNrXG4gICAgICBjb25zdCBjYWxsT2JqZWN0ID0gRGFpbHkuY3JlYXRlQ2FsbE9iamVjdCh7XG4gICAgICAgIHZpZGVvU291cmNlOiBmYWxzZSxcbiAgICAgICAgYXVkaW9Tb3VyY2U6IGF1ZGlvVHJhY2ssXG4gICAgICB9KTtcblxuICAgICAgdGhpcy5jYWxsT2JqZWN0ID0gY2FsbE9iamVjdDtcblxuICAgICAgdGhpcy5zZXR1cEV2ZW50SGFuZGxlcnMoY2FsbE9iamVjdCk7XG5cbiAgICAgIC8vIEpvaW4gcm9vbTsgRGFpbHkgaGFuZGxlcyBwbGF5YmFjayBvZiByZW1vdGUgKGJvdCkgYXVkaW8gYXV0b21hdGljYWxseS5cbiAgICAgIC8vIE9ubHkgcGFzcyB0b2tlbiB3aGVuIGl0J3MgYSBub24tZW1wdHkgc3RyaW5nIChEYWlseSByZWplY3RzIHVuZGVmaW5lZC9ub24tc3RyaW5nKS5cbiAgICAgIGNvbnN0IGpvaW5PcHRpb25zOiB7IHVybDogc3RyaW5nOyB0b2tlbj86IHN0cmluZyB9ID0geyB1cmw6IHJvb21VcmwgfTtcbiAgICAgIGlmICh0eXBlb2YgdG9rZW4gPT09ICdzdHJpbmcnICYmIHRva2VuLnRyaW0oKSAhPT0gJycpIHtcbiAgICAgICAgam9pbk9wdGlvbnMudG9rZW4gPSB0b2tlbjtcbiAgICAgIH1cbiAgICAgIGF3YWl0IGNhbGxPYmplY3Quam9pbihqb2luT3B0aW9ucyk7XG4gICAgICBjb25zb2xlLmxvZyhgW1ZvaWNlRGVidWddIFJvb20gY29ubmVjdGVkIChEYWlseSBqb2luIGNvbXBsZXRlKSDigJQgJHtuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCl9YCk7XG5cbiAgICAgIGNvbnN0IHBhcnRpY2lwYW50cyA9IGNhbGxPYmplY3QucGFydGljaXBhbnRzKCk7XG4gICAgICBpZiAocGFydGljaXBhbnRzPy5sb2NhbCkge1xuICAgICAgICB0aGlzLmxvY2FsU2Vzc2lvbklkID0gcGFydGljaXBhbnRzLmxvY2FsLnNlc3Npb25faWQ7XG4gICAgICB9XG5cbiAgICAgIC8vIFN0YXJ0IHdpdGggbWljIG11dGVkOyBWb2ljZUFnZW50U2VydmljZSBhdXRvLXVubXV0ZXMgYWZ0ZXIgZmlyc3QgcmVtb3RlIGF1ZGlvIGZyYW1lLlxuICAgICAgY2FsbE9iamVjdC5zZXRMb2NhbEF1ZGlvKGZhbHNlKTtcbiAgICAgIHRoaXMubWljTXV0ZWRTdWJqZWN0Lm5leHQodHJ1ZSk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICB0aGlzLmNsZWFudXAoKTtcbiAgICAgIHRocm93IGVycjtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHNldHVwRXZlbnRIYW5kbGVycyhjYWxsOiBEYWlseUNhbGwpOiB2b2lkIHtcbiAgICAvLyBhY3RpdmUtc3BlYWtlci1jaGFuZ2U6IHVzZWQgT05MWSBmb3IgdXNlciBzcGVha2luZyBkZXRlY3Rpb24uXG4gICAgLy8gQm90IHNwZWFraW5nIGlzIGRldGVjdGVkIGJ5IG91ciBvd24gQW5hbHlzZXJOb2RlIChpbnN0YW50LCBubyBkZWJvdW5jZSkuXG4gICAgY2FsbC5vbignYWN0aXZlLXNwZWFrZXItY2hhbmdlJywgKGV2ZW50OiB7IGFjdGl2ZVNwZWFrZXI/OiB7IHBlZXJJZD86IHN0cmluZyB9IH0pID0+IHtcbiAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB7XG4gICAgICAgIGNvbnN0IHBlZXJJZCA9IGV2ZW50Py5hY3RpdmVTcGVha2VyPy5wZWVySWQ7XG4gICAgICAgIGlmICghcGVlcklkIHx8ICF0aGlzLmxvY2FsU2Vzc2lvbklkKSB7XG4gICAgICAgICAgdGhpcy51c2VyU3BlYWtpbmdTdWJqZWN0Lm5leHQoZmFsc2UpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBpc0xvY2FsID0gcGVlcklkID09PSB0aGlzLmxvY2FsU2Vzc2lvbklkO1xuICAgICAgICB0aGlzLnVzZXJTcGVha2luZ1N1YmplY3QubmV4dChpc0xvY2FsKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgLy8gdHJhY2stc3RhcnRlZCAvIHRyYWNrLXN0b3BwZWQ6IHNldCB1cCByZW1vdGUgYXVkaW8gcGxheWJhY2sgKyBBbmFseXNlck5vZGUgbW9uaXRvci5cbiAgICBjYWxsLm9uKCd0cmFjay1zdGFydGVkJywgKGV2ZW50OiB7IHBhcnRpY2lwYW50PzogRGFpbHlQYXJ0aWNpcGFudCB8IG51bGw7IHR5cGU/OiBzdHJpbmc7IHRyYWNrPzogTWVkaWFTdHJlYW1UcmFjayB9KSA9PiB7XG4gICAgICB0aGlzLm5nWm9uZS5ydW4oKCkgPT4ge1xuICAgICAgICBjb25zdCBwID0gZXZlbnQ/LnBhcnRpY2lwYW50O1xuICAgICAgICBjb25zdCB0eXBlID0gZXZlbnQ/LnR5cGUgPz8gZXZlbnQ/LnRyYWNrPy5raW5kO1xuICAgICAgICBjb25zdCB0cmFjayA9IGV2ZW50Py50cmFjaztcbiAgICAgICAgaWYgKHAgJiYgIXAubG9jYWwgJiYgdHlwZSA9PT0gJ2F1ZGlvJykge1xuICAgICAgICAgIGNvbnNvbGUubG9nKGBbVm9pY2VEZWJ1Z10gR290IGF1ZGlvIHRyYWNrIGZyb20gYmFja2VuZCAodHJhY2stc3RhcnRlZCkg4oCUIHJlYWR5U3RhdGU9JHt0cmFjaz8ucmVhZHlTdGF0ZX0sIG11dGVkPSR7dHJhY2s/Lm11dGVkfSDigJQgJHtuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCl9YCk7XG4gICAgICAgICAgY29uc3QgYXVkaW9UcmFjayA9IHRyYWNrID8/IChwIGFzIHsgdHJhY2tzPzogeyBhdWRpbz86IHsgdHJhY2s/OiBNZWRpYVN0cmVhbVRyYWNrIH0gfSB9KS50cmFja3M/LmF1ZGlvPy50cmFjaztcbiAgICAgICAgICBpZiAoYXVkaW9UcmFjayAmJiB0eXBlb2YgYXVkaW9UcmFjayA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgIHRoaXMucGxheVJlbW90ZVRyYWNrKGF1ZGlvVHJhY2spO1xuICAgICAgICAgICAgdGhpcy5tb25pdG9yUmVtb3RlQXVkaW8oYXVkaW9UcmFjayk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIGNhbGwub24oJ3RyYWNrLXN0b3BwZWQnLCAoZXZlbnQ6IHsgcGFydGljaXBhbnQ/OiBEYWlseVBhcnRpY2lwYW50IHwgbnVsbDsgdHlwZT86IHN0cmluZzsgdHJhY2s/OiBNZWRpYVN0cmVhbVRyYWNrIH0pID0+IHtcbiAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB7XG4gICAgICAgIGNvbnN0IHAgPSBldmVudD8ucGFydGljaXBhbnQ7XG4gICAgICAgIGNvbnN0IHR5cGUgPSBldmVudD8udHlwZSA/PyBldmVudD8udHJhY2s/LmtpbmQ7XG4gICAgICAgIGlmIChwICYmICFwLmxvY2FsICYmIHR5cGUgPT09ICdhdWRpbycpIHtcbiAgICAgICAgICB0aGlzLnN0b3BSZW1vdGVBdWRpb01vbml0b3IoKTtcbiAgICAgICAgICB0aGlzLnN0b3BSZW1vdGVBdWRpbygpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIGNhbGwub24oJ2xlZnQtbWVldGluZycsICgpID0+IHtcbiAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB0aGlzLmNsZWFudXAoKSk7XG4gICAgfSk7XG5cbiAgICBjYWxsLm9uKCdlcnJvcicsIChldmVudD86IHsgZXJyb3JNc2c/OiBzdHJpbmcgfSkgPT4ge1xuICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHtcbiAgICAgICAgY29uc29sZS5lcnJvcignRGFpbHlWb2ljZUNsaWVudDogRGFpbHkgZXJyb3InLCBldmVudD8uZXJyb3JNc2cgPz8gZXZlbnQpO1xuICAgICAgICB0aGlzLmNsZWFudXAoKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFBsYXkgcmVtb3RlIChib3QpIGF1ZGlvIHRyYWNrIHZpYSBhIGRlZGljYXRlZCBhdWRpbyBlbGVtZW50LlxuICAgKiBSZXF1aXJlZCBpbiBtYW55IGJyb3dzZXJzIHdoZXJlIERhaWx5J3MgaW50ZXJuYWwgcGxheWJhY2sgZG9lcyBub3Qgb3V0cHV0IHRvIHNwZWFrZXJzLlxuICAgKi9cbiAgcHJpdmF0ZSBwbGF5UmVtb3RlVHJhY2sodHJhY2s6IE1lZGlhU3RyZWFtVHJhY2spOiB2b2lkIHtcbiAgICB0aGlzLnN0b3BSZW1vdGVBdWRpbygpO1xuICAgIHRyeSB7XG4gICAgICBjb25zb2xlLmxvZyhgW1ZvaWNlRGVidWddIHBsYXlSZW1vdGVUcmFjayBjYWxsZWQg4oCUIHRyYWNrLnJlYWR5U3RhdGU9JHt0cmFjay5yZWFkeVN0YXRlfSwgdHJhY2subXV0ZWQ9JHt0cmFjay5tdXRlZH0g4oCUICR7bmV3IERhdGUoKS50b0lTT1N0cmluZygpfWApO1xuXG4gICAgICB0cmFjay5vbnVubXV0ZSA9ICgpID0+IHtcbiAgICAgICAgY29uc29sZS5sb2coYFtWb2ljZURlYnVnXSBSZW1vdGUgYXVkaW8gdHJhY2sgVU5NVVRFRCAoYXVkaW8gZGF0YSBhcnJpdmluZykg4oCUICR7bmV3IERhdGUoKS50b0lTT1N0cmluZygpfWApO1xuICAgICAgfTtcblxuICAgICAgY29uc3Qgc3RyZWFtID0gbmV3IE1lZGlhU3RyZWFtKFt0cmFja10pO1xuICAgICAgY29uc3QgYXVkaW8gPSBuZXcgQXVkaW8oKTtcbiAgICAgIGF1ZGlvLmF1dG9wbGF5ID0gdHJ1ZTtcbiAgICAgIGF1ZGlvLnNyY09iamVjdCA9IHN0cmVhbTtcbiAgICAgIHRoaXMucmVtb3RlQXVkaW9FbGVtZW50ID0gYXVkaW87XG5cbiAgICAgIGF1ZGlvLm9ucGxheWluZyA9ICgpID0+IHtcbiAgICAgICAgY29uc29sZS5sb2coYFtWb2ljZURlYnVnXSBBdWRpbyBlbGVtZW50IFBMQVlJTkcgKGJyb3dzZXIgc3RhcnRlZCBwbGF5YmFjaykg4oCUICR7bmV3IERhdGUoKS50b0lTT1N0cmluZygpfWApO1xuICAgICAgfTtcblxuICAgICAgbGV0IGZpcnN0VGltZVVwZGF0ZSA9IHRydWU7XG4gICAgICBhdWRpby5vbnRpbWV1cGRhdGUgPSAoKSA9PiB7XG4gICAgICAgIGlmIChmaXJzdFRpbWVVcGRhdGUpIHtcbiAgICAgICAgICBmaXJzdFRpbWVVcGRhdGUgPSBmYWxzZTtcbiAgICAgICAgICBjb25zb2xlLmxvZyhgW1ZvaWNlRGVidWddIEF1ZGlvIGVsZW1lbnQgZmlyc3QgVElNRVVQREFURSAoYWN0dWFsIGF1ZGlvIG91dHB1dCkg4oCUICR7bmV3IERhdGUoKS50b0lTT1N0cmluZygpfWApO1xuICAgICAgICAgIHRoaXMuZmlyc3RSZW1vdGVBdWRpb0ZyYW1lU3ViamVjdC5uZXh0KHRydWUpO1xuICAgICAgICB9XG4gICAgICB9O1xuXG4gICAgICBjb25zdCBwID0gYXVkaW8ucGxheSgpO1xuICAgICAgaWYgKHAgJiYgdHlwZW9mIHAudGhlbiA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICBwLnRoZW4oKCkgPT4ge1xuICAgICAgICAgIGNvbnNvbGUubG9nKGBbVm9pY2VEZWJ1Z10gYXVkaW8ucGxheSgpIHJlc29sdmVkIOKAlCAke25ldyBEYXRlKCkudG9JU09TdHJpbmcoKX1gKTtcbiAgICAgICAgICB0aGlzLmZpcnN0UmVtb3RlQXVkaW9GcmFtZVN1YmplY3QubmV4dCh0cnVlKTtcbiAgICAgICAgfSkuY2F0Y2goKGVycikgPT4ge1xuICAgICAgICAgIGNvbnNvbGUud2FybignRGFpbHlWb2ljZUNsaWVudDogcmVtb3RlIGF1ZGlvIHBsYXkgZmFpbGVkIChtYXkgbmVlZCB1c2VyIGdlc3R1cmUpJywgZXJyKTtcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBjb25zb2xlLndhcm4oJ0RhaWx5Vm9pY2VDbGllbnQ6IGZhaWxlZCB0byBjcmVhdGUgcmVtb3RlIGF1ZGlvIGVsZW1lbnQnLCBlcnIpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBNb25pdG9yIHJlbW90ZSBhdWRpbyB0cmFjayBlbmVyZ3kgdmlhIEFuYWx5c2VyTm9kZS5cbiAgICogUG9sbHMgYXQgfjYwZnBzIGFuZCBmbGlwcyBzcGVha2luZ1N1YmplY3QgYmFzZWQgb24gYWN0dWFsIGF1ZGlvIGVuZXJneS5cbiAgICovXG4gIHByaXZhdGUgbW9uaXRvclJlbW90ZUF1ZGlvKHRyYWNrOiBNZWRpYVN0cmVhbVRyYWNrKTogdm9pZCB7XG4gICAgdGhpcy5zdG9wUmVtb3RlQXVkaW9Nb25pdG9yKCk7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGN0eCA9IG5ldyBBdWRpb0NvbnRleHQoKTtcbiAgICAgIGNvbnN0IHNvdXJjZSA9IGN0eC5jcmVhdGVNZWRpYVN0cmVhbVNvdXJjZShuZXcgTWVkaWFTdHJlYW0oW3RyYWNrXSkpO1xuICAgICAgY29uc3QgYW5hbHlzZXIgPSBjdHguY3JlYXRlQW5hbHlzZXIoKTtcbiAgICAgIGFuYWx5c2VyLmZmdFNpemUgPSAyNTY7XG4gICAgICBzb3VyY2UuY29ubmVjdChhbmFseXNlcik7XG4gICAgICB0aGlzLnJlbW90ZUF1ZGlvQ29udGV4dCA9IGN0eDtcblxuICAgICAgY29uc3QgZGF0YUFycmF5ID0gbmV3IFVpbnQ4QXJyYXkoYW5hbHlzZXIuZnJlcXVlbmN5QmluQ291bnQpO1xuICAgICAgY29uc3QgVEhSRVNIT0xEID0gNTtcbiAgICAgIGNvbnN0IFNJTEVOQ0VfTVMgPSAxNTAwO1xuICAgICAgbGV0IGxhc3RTb3VuZFRpbWUgPSAwO1xuICAgICAgbGV0IGlzU3BlYWtpbmcgPSBmYWxzZTtcblxuICAgICAgY29uc3QgcG9sbCA9ICgpID0+IHtcbiAgICAgICAgaWYgKCF0aGlzLnJlbW90ZUF1ZGlvQ29udGV4dCkgcmV0dXJuO1xuICAgICAgICBhbmFseXNlci5nZXRCeXRlRnJlcXVlbmN5RGF0YShkYXRhQXJyYXkpO1xuXG4gICAgICAgIGxldCBzdW0gPSAwO1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGRhdGFBcnJheS5sZW5ndGg7IGkrKykge1xuICAgICAgICAgIHN1bSArPSBkYXRhQXJyYXlbaV07XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgYXZnID0gc3VtIC8gZGF0YUFycmF5Lmxlbmd0aDtcblxuICAgICAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICAgICAgICBpZiAoYXZnID4gVEhSRVNIT0xEKSB7XG4gICAgICAgICAgbGFzdFNvdW5kVGltZSA9IG5vdztcbiAgICAgICAgICBpZiAoIWlzU3BlYWtpbmcpIHtcbiAgICAgICAgICAgIGlzU3BlYWtpbmcgPSB0cnVlO1xuICAgICAgICAgICAgY29uc29sZS5sb2coYFtWb2ljZURlYnVnXSBCb3QgYXVkaW8gZW5lcmd5IGRldGVjdGVkIChzcGVha2luZz10cnVlKSDigJQgYXZnPSR7YXZnLnRvRml4ZWQoMSl9IOKAlCAke25ldyBEYXRlKCkudG9JU09TdHJpbmcoKX1gKTtcbiAgICAgICAgICAgIHRoaXMubmdab25lLnJ1bigoKSA9PiB7XG4gICAgICAgICAgICAgIHRoaXMudXNlclNwZWFraW5nU3ViamVjdC5uZXh0KGZhbHNlKTtcbiAgICAgICAgICAgICAgdGhpcy5zcGVha2luZ1N1YmplY3QubmV4dCh0cnVlKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChpc1NwZWFraW5nICYmIG5vdyAtIGxhc3RTb3VuZFRpbWUgPiBTSUxFTkNFX01TKSB7XG4gICAgICAgICAgaXNTcGVha2luZyA9IGZhbHNlO1xuICAgICAgICAgIGNvbnNvbGUubG9nKGBbVm9pY2VEZWJ1Z10gQm90IGF1ZGlvIHNpbGVuY2UgZGV0ZWN0ZWQgKHNwZWFraW5nPWZhbHNlKSDigJQgJHtuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCl9YCk7XG4gICAgICAgICAgdGhpcy5uZ1pvbmUucnVuKCgpID0+IHRoaXMuc3BlYWtpbmdTdWJqZWN0Lm5leHQoZmFsc2UpKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMucmVtb3RlU3BlYWtpbmdSQUYgPSByZXF1ZXN0QW5pbWF0aW9uRnJhbWUocG9sbCk7XG4gICAgICB9O1xuXG4gICAgICB0aGlzLnJlbW90ZVNwZWFraW5nUkFGID0gcmVxdWVzdEFuaW1hdGlvbkZyYW1lKHBvbGwpO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgY29uc29sZS53YXJuKCdEYWlseVZvaWNlQ2xpZW50OiBmYWlsZWQgdG8gY3JlYXRlIHJlbW90ZSBhdWRpbyBtb25pdG9yJywgZXJyKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHN0b3BSZW1vdGVBdWRpb01vbml0b3IoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMucmVtb3RlU3BlYWtpbmdSQUYpIHtcbiAgICAgIGNhbmNlbEFuaW1hdGlvbkZyYW1lKHRoaXMucmVtb3RlU3BlYWtpbmdSQUYpO1xuICAgICAgdGhpcy5yZW1vdGVTcGVha2luZ1JBRiA9IG51bGw7XG4gICAgfVxuICAgIGlmICh0aGlzLnJlbW90ZUF1ZGlvQ29udGV4dCkge1xuICAgICAgdGhpcy5yZW1vdGVBdWRpb0NvbnRleHQuY2xvc2UoKS5jYXRjaCgoKSA9PiB7fSk7XG4gICAgICB0aGlzLnJlbW90ZUF1ZGlvQ29udGV4dCA9IG51bGw7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBzdG9wUmVtb3RlQXVkaW8oKTogdm9pZCB7XG4gICAgaWYgKHRoaXMucmVtb3RlQXVkaW9FbGVtZW50KSB7XG4gICAgICB0cnkge1xuICAgICAgICB0aGlzLnJlbW90ZUF1ZGlvRWxlbWVudC5wYXVzZSgpO1xuICAgICAgICB0aGlzLnJlbW90ZUF1ZGlvRWxlbWVudC5zcmNPYmplY3QgPSBudWxsO1xuICAgICAgfSBjYXRjaCAoXykge31cbiAgICAgIHRoaXMucmVtb3RlQXVkaW9FbGVtZW50ID0gbnVsbDtcbiAgICB9XG4gIH1cblxuICAvKiogU2V0IG1pYyBtdXRlZCBzdGF0ZS4gKi9cbiAgc2V0TXV0ZWQobXV0ZWQ6IGJvb2xlYW4pOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuY2FsbE9iamVjdCkgcmV0dXJuO1xuICAgIHRoaXMuY2FsbE9iamVjdC5zZXRMb2NhbEF1ZGlvKCFtdXRlZCk7XG4gICAgdGhpcy5taWNNdXRlZFN1YmplY3QubmV4dChtdXRlZCk7XG4gIH1cblxuICAvKiogRGlzY29ubmVjdCBhbmQgY2xlYW51cC4gKi9cbiAgYXN5bmMgZGlzY29ubmVjdCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIXRoaXMuY2FsbE9iamVjdCkge1xuICAgICAgdGhpcy5jbGVhbnVwKCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLmNhbGxPYmplY3QubGVhdmUoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAvLyBpZ25vcmVcbiAgICB9XG4gICAgdGhpcy5jbGVhbnVwKCk7XG4gIH1cblxuICBwcml2YXRlIGNsZWFudXAoKTogdm9pZCB7XG4gICAgdGhpcy5zdG9wUmVtb3RlQXVkaW9Nb25pdG9yKCk7XG4gICAgdGhpcy5zdG9wUmVtb3RlQXVkaW8oKTtcbiAgICBpZiAodGhpcy5jYWxsT2JqZWN0KSB7XG4gICAgICB0aGlzLmNhbGxPYmplY3QuZGVzdHJveSgpLmNhdGNoKCgpID0+IHt9KTtcbiAgICAgIHRoaXMuY2FsbE9iamVjdCA9IG51bGw7XG4gICAgfVxuICAgIGlmICh0aGlzLmxvY2FsU3RyZWFtKSB7XG4gICAgICB0aGlzLmxvY2FsU3RyZWFtLmdldFRyYWNrcygpLmZvckVhY2goKHQpID0+IHQuc3RvcCgpKTtcbiAgICAgIHRoaXMubG9jYWxTdHJlYW0gPSBudWxsO1xuICAgIH1cbiAgICB0aGlzLmxvY2FsU2Vzc2lvbklkID0gbnVsbDtcbiAgICB0aGlzLnNwZWFraW5nU3ViamVjdC5uZXh0KGZhbHNlKTtcbiAgICB0aGlzLnVzZXJTcGVha2luZ1N1YmplY3QubmV4dChmYWxzZSk7XG4gICAgdGhpcy5sb2NhbFN0cmVhbVN1YmplY3QubmV4dChudWxsKTtcbiAgICB0aGlzLmZpcnN0UmVtb3RlQXVkaW9GcmFtZVN1YmplY3QubmV4dChmYWxzZSk7XG4gICAgLy8gS2VlcCBsYXN0IG1pY011dGVkIHN0YXRlOyB3aWxsIHJlc2V0IG9uIG5leHQgY29ubmVjdFxuICB9XG59XG4iXX0=
|