genassist-chat-react 1.0.38 → 1.0.39
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/dist/components/ChatBubble.d.ts +1 -0
- package/dist/components/ChatBubble.js +4 -4
- package/dist/components/ChatMessage.js +6 -2
- package/dist/components/GenAgentChat.js +153 -28
- package/dist/components/LiveCallControl.d.ts +25 -0
- package/dist/components/LiveCallControl.js +83 -0
- package/dist/components/MarkdownMessage.js +23 -1
- package/dist/genassist-chat.es.js +16915 -0
- package/dist/genassist-chat.umd.js +91 -0
- package/dist/hooks/useAudioRecorder.js +74 -2
- package/dist/hooks/useChat.d.ts +3 -0
- package/dist/hooks/useChat.js +16 -5
- package/dist/hooks/useLiveVoice.d.ts +32 -0
- package/dist/hooks/useLiveVoice.js +292 -0
- package/dist/services/chatService.js +10 -6
- package/dist/styles/genAgentChatStyles.d.ts +2 -0
- package/dist/styles/genAgentChatStyles.js +15 -1
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.js +1 -1
- package/package.json +2 -2
- package/dist/components/AttachmentPreview.d.ts +0 -8
- package/dist/components/AttachmentPreview.js +0 -77
- package/dist/utils/urlUtil.d.ts +0 -2
- package/dist/utils/urlUtil.js +0 -7
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type LiveVoiceStatus = 'idle' | 'connecting' | 'listening' | 'speaking' | 'error';
|
|
2
|
+
interface UseLiveVoiceOptions {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
apiKey: string;
|
|
5
|
+
guestToken?: string | null;
|
|
6
|
+
tenant?: string;
|
|
7
|
+
agentId?: string | null;
|
|
8
|
+
conversationId?: string | null;
|
|
9
|
+
language?: string;
|
|
10
|
+
onError?: (error: Error) => void;
|
|
11
|
+
onInputTranscript?: (text: string) => void;
|
|
12
|
+
onOutputTranscript?: (text: string) => void;
|
|
13
|
+
onTurnComplete?: (turn: {
|
|
14
|
+
transcript: string;
|
|
15
|
+
response: string;
|
|
16
|
+
}) => void;
|
|
17
|
+
}
|
|
18
|
+
interface UseLiveVoiceReturn {
|
|
19
|
+
isActive: boolean;
|
|
20
|
+
status: LiveVoiceStatus;
|
|
21
|
+
start: () => Promise<void>;
|
|
22
|
+
stop: () => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Continuous, two-way voice conversation with a Voice Agent node.
|
|
26
|
+
*
|
|
27
|
+
* Streams 16 kHz mic PCM up a WebSocket to a persistent Gemini Live session and
|
|
28
|
+
* plays the 24 kHz reply audio back as it arrives (no record/stop/send). Supports
|
|
29
|
+
* barge-in: when the agent is interrupted, queued playback is flushed instantly.
|
|
30
|
+
*/
|
|
31
|
+
export declare function useLiveVoice(opts: UseLiveVoiceOptions): UseLiveVoiceReturn;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
12
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
+
function step(op) {
|
|
15
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
+
switch (op[0]) {
|
|
20
|
+
case 0: case 1: t = op; break;
|
|
21
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
+
default:
|
|
25
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
+
if (t[2]) _.ops.pop();
|
|
30
|
+
_.trys.pop(); continue;
|
|
31
|
+
}
|
|
32
|
+
op = body.call(thisArg, _);
|
|
33
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
38
|
+
import { createWebSocket } from '../utils/websocket';
|
|
39
|
+
var LIVE_INPUT_SAMPLE_RATE = 16000; // Gemini Live expects 16 kHz mono PCM in
|
|
40
|
+
var LIVE_OUTPUT_SAMPLE_RATE = 24000; // Gemini Live emits 24 kHz mono PCM out
|
|
41
|
+
var CAPTURE_BUFFER_SIZE = 4096;
|
|
42
|
+
/** Downsample a mono Float32 buffer to 16 kHz Int16 PCM (little-endian). */
|
|
43
|
+
function floatTo16kPcm(input, inputRate) {
|
|
44
|
+
var ratio = inputRate / LIVE_INPUT_SAMPLE_RATE;
|
|
45
|
+
var outLength = Math.floor(input.length / ratio);
|
|
46
|
+
var out = new Int16Array(outLength);
|
|
47
|
+
for (var i = 0; i < outLength; i++) {
|
|
48
|
+
var sample = input[Math.floor(i * ratio)] || 0;
|
|
49
|
+
var clamped = Math.max(-1, Math.min(1, sample));
|
|
50
|
+
out[i] = clamped < 0 ? clamped * 0x8000 : clamped * 0x7fff;
|
|
51
|
+
}
|
|
52
|
+
return out.buffer;
|
|
53
|
+
}
|
|
54
|
+
function buildLiveUrl(opts, threadId) {
|
|
55
|
+
// Live voice is a stateful 1:1 audio stream held by the backend, so the plugin
|
|
56
|
+
// connects directly to the backend (not the fan-out websocket service).
|
|
57
|
+
var wsBase = opts.baseUrl.replace(/^http/, 'ws').replace(/\/$/, '');
|
|
58
|
+
var auth = opts.guestToken
|
|
59
|
+
? "access_token=".concat(encodeURIComponent(opts.guestToken))
|
|
60
|
+
: "api_key=".concat(encodeURIComponent(opts.apiKey));
|
|
61
|
+
var params = [auth, "thread_id=".concat(encodeURIComponent(threadId))];
|
|
62
|
+
if (opts.tenant)
|
|
63
|
+
params.push("x-tenant-id=".concat(encodeURIComponent(opts.tenant)));
|
|
64
|
+
if (opts.language)
|
|
65
|
+
params.push("lang=".concat(encodeURIComponent(opts.language)));
|
|
66
|
+
return "".concat(wsBase, "/api/voice/live/").concat(opts.agentId, "?").concat(params.join('&'));
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Continuous, two-way voice conversation with a Voice Agent node.
|
|
70
|
+
*
|
|
71
|
+
* Streams 16 kHz mic PCM up a WebSocket to a persistent Gemini Live session and
|
|
72
|
+
* plays the 24 kHz reply audio back as it arrives (no record/stop/send). Supports
|
|
73
|
+
* barge-in: when the agent is interrupted, queued playback is flushed instantly.
|
|
74
|
+
*/
|
|
75
|
+
export function useLiveVoice(opts) {
|
|
76
|
+
var _this = this;
|
|
77
|
+
var _a = useState(false), isActive = _a[0], setIsActive = _a[1];
|
|
78
|
+
var _b = useState('idle'), status = _b[0], setStatus = _b[1];
|
|
79
|
+
var wsRef = useRef(null);
|
|
80
|
+
var streamRef = useRef(null);
|
|
81
|
+
var captureCtxRef = useRef(null);
|
|
82
|
+
var processorRef = useRef(null);
|
|
83
|
+
var playbackCtxRef = useRef(null);
|
|
84
|
+
var playheadRef = useRef(0);
|
|
85
|
+
var sourcesRef = useRef([]);
|
|
86
|
+
var speakingTimerRef = useRef(null);
|
|
87
|
+
// Mirror `isActive` into a ref so async callbacks (e.g. the speaking timer)
|
|
88
|
+
// can read the latest value without being re-created.
|
|
89
|
+
var isActiveRef = useRef(false);
|
|
90
|
+
isActiveRef.current = isActive;
|
|
91
|
+
// Keep the latest callbacks without re-creating start/stop.
|
|
92
|
+
var optsRef = useRef(opts);
|
|
93
|
+
optsRef.current = opts;
|
|
94
|
+
var flushPlayback = useCallback(function () {
|
|
95
|
+
sourcesRef.current.forEach(function (src) {
|
|
96
|
+
try {
|
|
97
|
+
src.stop();
|
|
98
|
+
}
|
|
99
|
+
catch (_a) {
|
|
100
|
+
/* already stopped */
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
sourcesRef.current = [];
|
|
104
|
+
if (playbackCtxRef.current) {
|
|
105
|
+
playheadRef.current = playbackCtxRef.current.currentTime;
|
|
106
|
+
}
|
|
107
|
+
}, []);
|
|
108
|
+
var stop = useCallback(function () {
|
|
109
|
+
var _a, _b, _c, _d;
|
|
110
|
+
if (speakingTimerRef.current)
|
|
111
|
+
clearTimeout(speakingTimerRef.current);
|
|
112
|
+
flushPlayback();
|
|
113
|
+
(_a = processorRef.current) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
114
|
+
processorRef.current = null;
|
|
115
|
+
(_b = streamRef.current) === null || _b === void 0 ? void 0 : _b.getTracks().forEach(function (t) { return t.stop(); });
|
|
116
|
+
streamRef.current = null;
|
|
117
|
+
(_c = captureCtxRef.current) === null || _c === void 0 ? void 0 : _c.close().catch(function () { return undefined; });
|
|
118
|
+
captureCtxRef.current = null;
|
|
119
|
+
(_d = playbackCtxRef.current) === null || _d === void 0 ? void 0 : _d.close().catch(function () { return undefined; });
|
|
120
|
+
playbackCtxRef.current = null;
|
|
121
|
+
if (wsRef.current) {
|
|
122
|
+
wsRef.current.onclose = null;
|
|
123
|
+
try {
|
|
124
|
+
wsRef.current.close();
|
|
125
|
+
}
|
|
126
|
+
catch (_e) {
|
|
127
|
+
/* noop */
|
|
128
|
+
}
|
|
129
|
+
wsRef.current = null;
|
|
130
|
+
}
|
|
131
|
+
setIsActive(false);
|
|
132
|
+
setStatus('idle');
|
|
133
|
+
}, [flushPlayback]);
|
|
134
|
+
var playPcmChunk = useCallback(function (pcm) {
|
|
135
|
+
var ctx = playbackCtxRef.current;
|
|
136
|
+
if (!ctx)
|
|
137
|
+
return;
|
|
138
|
+
var ints = new Int16Array(pcm);
|
|
139
|
+
if (ints.length === 0)
|
|
140
|
+
return;
|
|
141
|
+
var floats = new Float32Array(ints.length);
|
|
142
|
+
for (var i = 0; i < ints.length; i++)
|
|
143
|
+
floats[i] = ints[i] / 0x8000;
|
|
144
|
+
var buffer = ctx.createBuffer(1, floats.length, LIVE_OUTPUT_SAMPLE_RATE);
|
|
145
|
+
buffer.copyToChannel(floats, 0);
|
|
146
|
+
var source = ctx.createBufferSource();
|
|
147
|
+
source.buffer = buffer;
|
|
148
|
+
source.connect(ctx.destination);
|
|
149
|
+
var startAt = Math.max(ctx.currentTime, playheadRef.current);
|
|
150
|
+
source.start(startAt);
|
|
151
|
+
playheadRef.current = startAt + buffer.duration;
|
|
152
|
+
sourcesRef.current.push(source);
|
|
153
|
+
source.onended = function () {
|
|
154
|
+
sourcesRef.current = sourcesRef.current.filter(function (s) { return s !== source; });
|
|
155
|
+
};
|
|
156
|
+
// Reflect "speaking" until the scheduled audio drains.
|
|
157
|
+
setStatus('speaking');
|
|
158
|
+
if (speakingTimerRef.current)
|
|
159
|
+
clearTimeout(speakingTimerRef.current);
|
|
160
|
+
var remainingMs = (playheadRef.current - ctx.currentTime) * 1000 + 150;
|
|
161
|
+
speakingTimerRef.current = setTimeout(function () {
|
|
162
|
+
if (isActiveRef.current)
|
|
163
|
+
setStatus('listening');
|
|
164
|
+
}, remainingMs);
|
|
165
|
+
}, []);
|
|
166
|
+
var handleEvent = useCallback(function (data) {
|
|
167
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
168
|
+
var cb = optsRef.current;
|
|
169
|
+
switch (data.type) {
|
|
170
|
+
case 'ready':
|
|
171
|
+
setStatus('listening');
|
|
172
|
+
break;
|
|
173
|
+
case 'input_transcript':
|
|
174
|
+
if (typeof data.text === 'string')
|
|
175
|
+
(_a = cb.onInputTranscript) === null || _a === void 0 ? void 0 : _a.call(cb, data.text);
|
|
176
|
+
break;
|
|
177
|
+
case 'output_transcript':
|
|
178
|
+
if (typeof data.text === 'string')
|
|
179
|
+
(_b = cb.onOutputTranscript) === null || _b === void 0 ? void 0 : _b.call(cb, data.text);
|
|
180
|
+
break;
|
|
181
|
+
case 'interrupted':
|
|
182
|
+
flushPlayback();
|
|
183
|
+
setStatus('listening');
|
|
184
|
+
break;
|
|
185
|
+
case 'turn_complete':
|
|
186
|
+
(_c = cb.onTurnComplete) === null || _c === void 0 ? void 0 : _c.call(cb, {
|
|
187
|
+
transcript: String((_d = data.transcript) !== null && _d !== void 0 ? _d : ''),
|
|
188
|
+
response: String((_e = data.response) !== null && _e !== void 0 ? _e : ''),
|
|
189
|
+
});
|
|
190
|
+
break;
|
|
191
|
+
case 'error':
|
|
192
|
+
(_f = cb.onError) === null || _f === void 0 ? void 0 : _f.call(cb, new Error(String((_g = data.message) !== null && _g !== void 0 ? _g : 'Live voice error')));
|
|
193
|
+
setStatus('error');
|
|
194
|
+
break;
|
|
195
|
+
default:
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
}, [flushPlayback]);
|
|
199
|
+
var start = useCallback(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
200
|
+
var cb, threadId, stream_1, AudioCtx, captureCtx_1, ws_1, err_1;
|
|
201
|
+
var _a, _b;
|
|
202
|
+
return __generator(this, function (_c) {
|
|
203
|
+
switch (_c.label) {
|
|
204
|
+
case 0:
|
|
205
|
+
cb = optsRef.current;
|
|
206
|
+
if (!cb.agentId) {
|
|
207
|
+
(_a = cb.onError) === null || _a === void 0 ? void 0 : _a.call(cb, new Error('Live voice unavailable: agent is still loading'));
|
|
208
|
+
return [2 /*return*/];
|
|
209
|
+
}
|
|
210
|
+
if (wsRef.current)
|
|
211
|
+
return [2 /*return*/]; // already active
|
|
212
|
+
setStatus('connecting');
|
|
213
|
+
// Persistence (transcript/dashboard) keys off an existing conversation UUID.
|
|
214
|
+
// Without one the call still works, but the backend skips saving the turns.
|
|
215
|
+
if (!cb.conversationId) {
|
|
216
|
+
console.warn('[useLiveVoice] No conversationId yet — the live call will run but its ' +
|
|
217
|
+
'transcripts will not be persisted.');
|
|
218
|
+
}
|
|
219
|
+
threadId = cb.conversationId ||
|
|
220
|
+
(typeof crypto !== 'undefined' && 'randomUUID' in crypto
|
|
221
|
+
? crypto.randomUUID()
|
|
222
|
+
: "live-".concat(Date.now()));
|
|
223
|
+
_c.label = 1;
|
|
224
|
+
case 1:
|
|
225
|
+
_c.trys.push([1, 3, , 4]);
|
|
226
|
+
return [4 /*yield*/, navigator.mediaDevices.getUserMedia({
|
|
227
|
+
audio: { channelCount: 1, echoCancellation: true, noiseSuppression: true },
|
|
228
|
+
})];
|
|
229
|
+
case 2:
|
|
230
|
+
stream_1 = _c.sent();
|
|
231
|
+
streamRef.current = stream_1;
|
|
232
|
+
AudioCtx = window.AudioContext ||
|
|
233
|
+
window.webkitAudioContext;
|
|
234
|
+
captureCtx_1 = new AudioCtx();
|
|
235
|
+
captureCtxRef.current = captureCtx_1;
|
|
236
|
+
playbackCtxRef.current = new AudioCtx();
|
|
237
|
+
playheadRef.current = playbackCtxRef.current.currentTime;
|
|
238
|
+
ws_1 = createWebSocket(buildLiveUrl(cb, threadId));
|
|
239
|
+
ws_1.binaryType = 'arraybuffer';
|
|
240
|
+
wsRef.current = ws_1;
|
|
241
|
+
ws_1.onopen = function () {
|
|
242
|
+
var source = captureCtx_1.createMediaStreamSource(stream_1);
|
|
243
|
+
// TODO: migrate to AudioWorklet — ScriptProcessorNode is deprecated and
|
|
244
|
+
// runs the downsample on the main thread (can jank on slow devices).
|
|
245
|
+
var processor = captureCtx_1.createScriptProcessor(CAPTURE_BUFFER_SIZE, 1, 1);
|
|
246
|
+
processorRef.current = processor;
|
|
247
|
+
processor.onaudioprocess = function (e) {
|
|
248
|
+
if (ws_1.readyState !== ws_1.OPEN)
|
|
249
|
+
return;
|
|
250
|
+
var pcm = floatTo16kPcm(e.inputBuffer.getChannelData(0), captureCtx_1.sampleRate);
|
|
251
|
+
ws_1.send(pcm);
|
|
252
|
+
};
|
|
253
|
+
source.connect(processor);
|
|
254
|
+
processor.connect(captureCtx_1.destination); // required to drive onaudioprocess
|
|
255
|
+
setIsActive(true);
|
|
256
|
+
setStatus('listening');
|
|
257
|
+
};
|
|
258
|
+
ws_1.onmessage = function (event) {
|
|
259
|
+
if (event.data instanceof ArrayBuffer) {
|
|
260
|
+
playPcmChunk(event.data);
|
|
261
|
+
}
|
|
262
|
+
else if (typeof event.data === 'string') {
|
|
263
|
+
try {
|
|
264
|
+
handleEvent(JSON.parse(event.data));
|
|
265
|
+
}
|
|
266
|
+
catch (_a) {
|
|
267
|
+
/* ignore malformed event */
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
ws_1.onerror = function () {
|
|
272
|
+
var _a;
|
|
273
|
+
(_a = cb.onError) === null || _a === void 0 ? void 0 : _a.call(cb, new Error('Live voice connection error'));
|
|
274
|
+
};
|
|
275
|
+
ws_1.onclose = function () {
|
|
276
|
+
stop();
|
|
277
|
+
};
|
|
278
|
+
return [3 /*break*/, 4];
|
|
279
|
+
case 3:
|
|
280
|
+
err_1 = _c.sent();
|
|
281
|
+
(_b = cb.onError) === null || _b === void 0 ? void 0 : _b.call(cb, err_1 instanceof Error ? err_1 : new Error(String(err_1)));
|
|
282
|
+
stop();
|
|
283
|
+
return [3 /*break*/, 4];
|
|
284
|
+
case 4: return [2 /*return*/];
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}); }, [handleEvent, playPcmChunk, stop]);
|
|
288
|
+
// Tear down on unmount.
|
|
289
|
+
useEffect(function () { return function () { return stop(); }; }, [stop]);
|
|
290
|
+
return { isActive: isActive, status: status, start: start, stop: stop };
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXNlTGl2ZVZvaWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2hvb2tzL3VzZUxpdmVWb2ljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxPQUFPLEVBQUUsV0FBVyxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLE1BQU0sT0FBTyxDQUFDO0FBQ2pFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUVyRCxJQUFNLHNCQUFzQixHQUFHLEtBQUssQ0FBQyxDQUFDLHlDQUF5QztBQUMvRSxJQUFNLHVCQUF1QixHQUFHLEtBQUssQ0FBQyxDQUFDLHdDQUF3QztBQUMvRSxJQUFNLG1CQUFtQixHQUFHLElBQUksQ0FBQztBQWlDakMsNEVBQTRFO0FBQzVFLFNBQVMsYUFBYSxDQUFDLEtBQW1CLEVBQUUsU0FBaUI7SUFDM0QsSUFBTSxLQUFLLEdBQUcsU0FBUyxHQUFHLHNCQUFzQixDQUFDO0lBQ2pELElBQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUMsQ0FBQztJQUNuRCxJQUFNLEdBQUcsR0FBRyxJQUFJLFVBQVUsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUN0QyxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsU0FBUyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDbkMsSUFBTSxNQUFNLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLEtBQUssQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ2pELElBQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztRQUNsRCxHQUFH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T0FBQSxFQUFFLElBQUksTUFBQSxFQUFFLENBQUM7QUFDM0MsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHVzZUNhbGxiYWNrLCB1c2VFZmZlY3QsIHVzZVJlZiwgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCc7XG5pbXBvcnQgeyBjcmVhdGVXZWJTb2NrZXQgfSBmcm9tICcuLi91dGlscy93ZWJzb2NrZXQnO1xuXG5jb25zdCBMSVZFX0lOUFVUX1NBTVBMRV9SQVRFID0gMTYwMDA7IC8vIEdlbWluaSBMaXZlIGV4cGVjdHMgMTYga0h6IG1vbm8gUENNIGluXG5jb25zdCBMSVZFX09VVFBVVF9TQU1QTEVfUkFURSA9IDI0MDAwOyAvLyBHZW1pbmkgTGl2ZSBlbWl0cyAyNCBrSHogbW9ubyBQQ00gb3V0XG5jb25zdCBDQVBUVVJFX0JVRkZFUl9TSVpFID0gNDA5NjtcblxuZXhwb3J0IHR5cGUgTGl2ZVZvaWNlU3RhdHVzID1cbiAgfCAnaWRsZSdcbiAgfCAnY29ubmVjdGluZydcbiAgfCAnbGlzdGVuaW5nJ1xuICB8ICdzcGVha2luZydcbiAgfCAnZXJyb3InO1xuXG5pbnRlcmZhY2UgVXNlTGl2ZVZvaWNlT3B0aW9ucyB7XG4gIC8vIEJhY2tlbmQgYmFzZSBVUkwgKGh0dHAvaHR0cHMpLiBMaXZlIHZvaWNlIGlzIGEgc3RhdGVmdWwgMToxIGF1ZGlvIHN0cmVhbVxuICAvLyBoZWxkIGJ5IHRoZSBiYWNrZW5kLCBzbyB0aGUgcGx1Z2luIGNvbm5lY3RzIHRoZXJlIGRpcmVjdGx5ICh0aGUgZmFuLW91dFxuICAvLyB3ZWJzb2NrZXQgc2VydmljZSBpcyBub3QgaW52b2x2ZWQpLlxuICBiYXNlVXJsOiBzdHJpbmc7XG4gIGFwaUtleTogc3RyaW5nO1xuICBndWVzdFRva2VuPzogc3RyaW5nIHwgbnVsbDtcbiAgdGVuYW50Pzogc3RyaW5nO1xuICBhZ2VudElkPzogc3RyaW5nIHwgbnVsbDtcbiAgY29udmVyc2F0aW9uSWQ/OiBzdHJpbmcgfCBudWxsO1xuICBsYW5ndWFnZT86IHN0cmluZztcbiAgb25FcnJvcj86IChlcnJvcjogRXJyb3IpID0+IHZvaWQ7XG4gIG9uSW5wdXRUcmFuc2NyaXB0PzogKHRleHQ6IHN0cmluZykgPT4gdm9pZDtcbiAgb25PdXRwdXRUcmFuc2NyaXB0PzogKHRleHQ6IHN0cmluZykgPT4gdm9pZDtcbiAgb25UdXJuQ29tcGxldGU/OiAodHVybjogeyB0cmFuc2NyaXB0OiBzdHJpbmc7IHJlc3BvbnNlOiBzdHJpbmcgfSkgPT4gdm9pZDtcbn1cblxuaW50ZXJmYWNlIFVzZUxpdmVWb2ljZVJldHVybiB7XG4gIGlzQWN0aXZlOiBib29sZWFuO1xuICBzdGF0dXM6IExpdmVWb2ljZVN0YXR1cztcbiAgc3RhcnQ6ICgpID0+IFByb21pc2U8dm9pZD47XG4gIHN0b3A6ICgpID0+IHZvaWQ7XG59XG5cbi8qKiBEb3duc2FtcGxlIGEgbW9ubyBGbG9hdDMyIGJ1ZmZlciB0byAxNiBrSHogSW50MTYgUENNIChsaXR0bGUtZW5kaWFuKS4gKi9cbmZ1bmN0aW9uIGZsb2F0VG8xNmtQY20oaW5wdXQ6IEZsb2F0MzJBcnJheSwgaW5wdXRSYXRlOiBudW1iZXIpOiBBcnJheUJ1ZmZlciB7XG4gIGNvbnN0IHJhdGlvID0gaW5wdXRSYXRlIC8gTElWRV9JTlBVVF9TQU1QTEVfUkFURTtcbiAgY29uc3Qgb3V0TGVuZ3RoID0gTWF0aC5mbG9vcihpbnB1dC5sZW5ndGggLyByYXRpbyk7XG4gIGNvbnN0IG91dCA9IG5ldyBJbnQxNkFycmF5KG91dExlbmd0aCk7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgb3V0TGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBzYW1wbGUgPSBpbnB1dFtNYXRoLmZsb29yKGkgKiByYXRpbyldIHx8IDA7XG4gICAgY29uc3QgY2xhbXBlZCA9IE1hdGgubWF4KC0xLCBNYXRoLm1pbigxLCBzYW1wbGUpKTtcbiAgICBvdXRbaV0gPSBjbGFtcGVkIDwgMCA/IGNsYW1wZWQgKiAweDgwMDAgOiBjbGFtcGVkICogMHg3ZmZmO1xuICB9XG4gIHJldHVybiBvdXQuYnVmZmVyO1xufVxuXG5mdW5jdGlvbiBidWlsZExpdmVVcmwob3B0czogVXNlTGl2ZVZvaWNlT3B0aW9ucywgdGhyZWFkSWQ6IHN0cmluZyk6IHN0cmluZyB7XG4gIC8vIExpdmUgdm9pY2UgaXMgYSBzdGF0ZWZ1bCAxOjEgYXVkaW8gc3RyZWFtIGhlbGQgYnkgdGhlIGJhY2tlbmQsIHNvIHRoZSBwbHVnaW5cbiAgLy8gY29ubmVjdHMgZGlyZWN0bHkgdG8gdGhlIGJhY2tlbmQgKG5vdCB0aGUgZmFuLW91dCB3ZWJzb2NrZXQgc2VydmljZSkuXG4gIGNvbnN0IHdzQmFzZSA9IG9wdHMuYmFzZVVybC5yZXBsYWNlKC9eaHR0cC8sICd3cycpLnJlcGxhY2UoL1xcLyQvLCAnJyk7XG4gIGNvbnN0IGF1dGggPSBvcHRzLmd1ZXN0VG9rZW5cbiAgICA/IGBhY2Nlc3NfdG9rZW49JHtlbmNvZGVVUklDb21wb25lbnQob3B0cy5ndWVzdFRva2VuKX1gXG4gICAgOiBgYXBpX2tleT0ke2VuY29kZVVSSUNvbXBvbmVudChvcHRzLmFwaUtleSl9YDtcbiAgY29uc3QgcGFyYW1zID0gW2F1dGgsIGB0aHJlYWRfaWQ9JHtlbmNvZGVVUklDb21wb25lbnQodGhyZWFkSWQpfWBdO1xuICBpZiAob3B0cy50ZW5hbnQpIHBhcmFtcy5wdXNoKGB4LXRlbmFudC1pZD0ke2VuY29kZVVSSUNvbXBvbmVudChvcHRzLnRlbmFudCl9YCk7XG4gIGlmIChvcHRzLmxhbmd1YWdlKSBwYXJhbXMucHVzaChgbGFuZz0ke2VuY29kZVVSSUNvbXBvbmVudChvcHRzLmxhbmd1YWdlKX1gKTtcbiAgcmV0dXJuIGAke3dzQmFzZX0vYXBpL3ZvaWNlL2xpdmUvJHtvcHRzLmFnZW50SWR9PyR7cGFyYW1zLmpvaW4oJyYnKX1gO1xufVxuXG4vKipcbiAqIENvbnRpbnVvdXMsIHR3by13YXkgdm9pY2UgY29udmVyc2F0aW9uIHdpdGggYSBWb2ljZSBBZ2VudCBub2RlLlxuICpcbiAqIFN0cmVhbXMgMTYga0h6IG1pYyBQQ00gdXAgYSBXZWJTb2NrZXQgdG8gYSBwZXJzaXN0ZW50IEdlbWluaSBMaXZlIHNlc3Npb24gYW5kXG4gKiBwbGF5cyB0aGUgMjQga0h6IHJlcGx5IGF1ZGlvIGJhY2sgYXMgaXQgYXJyaXZlcyAobm8gcmVjb3JkL3N0b3Avc2VuZCkuIFN1cHBvcnRzXG4gKiBiYXJnZS1pbjogd2hlbiB0aGUgYWdlbnQgaXMgaW50ZXJydXB0ZWQsIHF1ZXVlZCBwbGF5YmFjayBpcyBmbHVzaGVkIGluc3RhbnRseS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHVzZUxpdmVWb2ljZShvcHRzOiBVc2VMaXZlVm9pY2VPcHRpb25zKTogVXNlTGl2ZVZvaWNlUmV0dXJuIHtcbiAgY29uc3QgW2lzQWN0aXZlLCBzZXRJc0FjdGl2ZV0gPSB1c2VTdGF0ZShmYWxzZSk7XG4gIGNvbnN0IFtzdGF0dXMsIHNldFN0YXR1c10gPSB1c2VTdGF0ZTxMaXZlVm9pY2VTdGF0dXM+KCdpZGxlJyk7XG5cbiAgY29uc3Qgd3NSZWYgPSB1c2VSZWY8V2ViU29ja2V0IHwgbnVsbD4obnVsbCk7XG4gIGNvbnN0IHN0cmVhbVJlZiA9IHVzZVJlZjxNZWRpYVN0cmVhbSB8IG51bGw+KG51bGwpO1xuICBjb25zdCBjYXB0dXJlQ3R4UmVmID0gdXNlUmVmPEF1ZGlvQ29udGV4dCB8IG51bGw+KG51bGwpO1xuICBjb25zdCBwcm9jZXNzb3JSZWYgPSB1c2VSZWY8U2NyaXB0UHJvY2Vzc29yTm9kZSB8IG51bGw+KG51bGwpO1xuICBjb25zdCBwbGF5YmFja0N0eFJlZiA9IHVzZVJlZjxBdWRpb0NvbnRleHQgfCBudWxsPihudWxsKTtcbiAgY29uc3QgcGxheWhlYWRSZWYgPSB1c2VSZWY8bnVtYmVyPigwKTtcbiAgY29uc3Qgc291cmNlc1JlZiA9IHVzZVJlZjxBdWRpb0J1ZmZlclNvdXJjZU5vZGVbXT4oW10pO1xuICBjb25zdCBzcGVha2luZ1RpbWVyUmVmID0gdXNlUmVmPFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+IHwgbnVsbD4obnVsbCk7XG5cbiAgLy8gTWlycm9yIGBpc0FjdGl2ZWAgaW50byBhIHJlZiBzbyBhc3luYyBjYWxsYmFja3MgKGUuZy4gdGhlIHNwZWFraW5nIHRpbWVyKVxuICAvLyBjYW4gcmVhZCB0aGUgbGF0ZXN0IHZhbHVlIHdpdGhvdXQgYmVpbmcgcmUtY3JlYXRlZC5cbiAgY29uc3QgaXNBY3RpdmVSZWYgPSB1c2VSZWYoZmFsc2UpO1xuICBpc0FjdGl2ZVJlZi5jdXJyZW50ID0gaXNBY3RpdmU7XG5cbiAgLy8gS2VlcCB0aGUgbGF0ZXN0IGNhbGxiYWNrcyB3aXRob3V0IHJlLWNyZWF0aW5nIHN0YXJ0L3N0b3AuXG4gIGNvbnN0IG9wdHNSZWYgPSB1c2VSZWYob3B0cyk7XG4gIG9wdHNSZWYuY3VycmVudCA9IG9wdHM7XG5cbiAgY29uc3QgZmx1c2hQbGF5YmFjayA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBzb3VyY2VzUmVmLmN1cnJlbnQuZm9yRWFjaCgoc3JjKSA9PiB7XG4gICAgICB0cnkge1xuICAgICAgICBzcmMuc3RvcCgpO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIC8qIGFscmVhZHkgc3RvcHBlZCAqL1xuICAgICAgfVxuICAgIH0pO1xuICAgIHNvdXJjZXNSZWYuY3VycmVudCA9IFtdO1xuICAgIGlmIChwbGF5YmFja0N0eFJlZi5jdXJyZW50KSB7XG4gICAgICBwbGF5aGVhZFJlZi5jdXJyZW50ID0gcGxheWJhY2tDdHhSZWYuY3VycmVudC5jdXJyZW50VGltZTtcbiAgICB9XG4gIH0sIFtdKTtcblxuICBjb25zdCBzdG9wID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGlmIChzcGVha2luZ1RpbWVyUmVmLmN1cnJlbnQpIGNsZWFyVGltZW91dChzcGVha2luZ1RpbWVyUmVmLmN1cnJlbnQpO1xuICAgIGZsdXNoUGxheWJhY2soKTtcbiAgICBwcm9jZXNzb3JSZWYuY3VycmVudD8uZGlzY29ubmVjdCgpO1xuICAgIHByb2Nlc3NvclJlZi5jdXJyZW50ID0gbnVsbDtcbiAgICBzdHJlYW1SZWYuY3VycmVudD8uZ2V0VHJhY2tzKCkuZm9yRWFjaCgodCkgPT4gdC5zdG9wKCkpO1xuICAgIHN0cmVhbVJlZi5jdXJyZW50ID0gbnVsbDtcbiAgICBjYXB0dXJlQ3R4UmVmLmN1cnJlbnQ/LmNsb3NlKCkuY2F0Y2goKCkgPT4gdW5kZWZpbmVkKTtcbiAgICBjYXB0dXJlQ3R4UmVmLmN1cnJlbnQgPSBudWxsO1xuICAgIHBsYXliYWNrQ3R4UmVmLmN1cnJlbnQ/LmNsb3NlKCkuY2F0Y2goKCkgPT4gdW5kZWZpbmVkKTtcbiAgICBwbGF5YmFja0N0eFJlZi5jdXJyZW50ID0gbnVsbDtcbiAgICBpZiAod3NSZWYuY3VycmVudCkge1xuICAgICAgd3NSZWYuY3VycmVudC5vbmNsb3NlID0gbnVsbDtcbiAgICAgIHRyeSB7XG4gICAgICAgIHdzUmVmLmN1cnJlbnQuY2xvc2UoKTtcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICAvKiBub29wICovXG4gICAgICB9XG4gICAgICB3c1JlZi5jdXJyZW50ID0gbnVsbDtcbiAgICB9XG4gICAgc2V0SXNBY3RpdmUoZmFsc2UpO1xuICAgIHNldFN0YXR1cygnaWRsZScpO1xuICB9LCBbZmx1c2hQbGF5YmFja10pO1xuXG4gIGNvbnN0IHBsYXlQY21DaHVuayA9IHVzZUNhbGxiYWNrKChwY206IEFycmF5QnVmZmVyKSA9PiB7XG4gICAgY29uc3QgY3R4ID0gcGxheWJhY2tDdHhSZWYuY3VycmVudDtcbiAgICBpZiAoIWN0eCkgcmV0dXJuO1xuICAgIGNvbnN0IGludHMgPSBuZXcgSW50MTZBcnJheShwY20pO1xuICAgIGlmIChpbnRzLmxlbmd0aCA9PT0gMCkgcmV0dXJuO1xuICAgIGNvbnN0IGZsb2F0cyA9IG5ldyBGbG9hdDMyQXJyYXkoaW50cy5sZW5ndGgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgaW50cy5sZW5ndGg7IGkrKykgZmxvYXRzW2ldID0gaW50c1tpXSAvIDB4ODAwMDtcblxuICAgIGNvbnN0IGJ1ZmZlciA9IGN0eC5jcmVhdGVCdWZmZXIoMSwgZmxvYXRzLmxlbmd0aCwgTElWRV9PVVRQVVRfU0FNUExFX1JBVEUpO1xuICAgIGJ1ZmZlci5jb3B5VG9DaGFubmVsKGZsb2F0cywgMCk7XG4gICAgY29uc3Qgc291cmNlID0gY3R4LmNyZWF0ZUJ1ZmZlclNvdXJjZSgpO1xuICAgIHNvdXJjZS5idWZmZXIgPSBidWZmZXI7XG4gICAgc291cmNlLmNvbm5lY3QoY3R4LmRlc3RpbmF0aW9uKTtcblxuICAgIGNvbnN0IHN0YXJ0QXQgPSBNYXRoLm1heChjdHguY3VycmVudFRpbWUsIHBsYXloZWFkUmVmLmN1cnJlbnQpO1xuICAgIHNvdXJjZS5zdGFydChzdGFydEF0KTtcbiAgICBwbGF5aGVhZFJlZi5jdXJyZW50ID0gc3RhcnRBdCArIGJ1ZmZlci5kdXJhdGlvbjtcbiAgICBzb3VyY2VzUmVmLmN1cnJlbnQucHVzaChzb3VyY2UpO1xuICAgIHNvdXJjZS5vbmVuZGVkID0gKCkgPT4ge1xuICAgICAgc291cmNlc1JlZi5jdXJyZW50ID0gc291cmNlc1JlZi5jdXJyZW50LmZpbHRlcigocykgPT4gcyAhPT0gc291cmNlKTtcbiAgICB9O1xuXG4gICAgLy8gUmVmbGVjdCBcInNwZWFraW5nXCIgdW50aWwgdGhlIHNjaGVkdWxlZCBhdWRpbyBkcmFpbnMuXG4gICAgc2V0U3RhdHVzKCdzcGVha2luZycpO1xuICAgIGlmIChzcGVha2luZ1RpbWVyUmVmLmN1cnJlbnQpIGNsZWFyVGltZW91dChzcGVha2luZ1RpbWVyUmVmLmN1cnJlbnQpO1xuICAgIGNvbnN0IHJlbWFpbmluZ01zID0gKHBsYXloZWFkUmVmLmN1cnJlbnQgLSBjdHguY3VycmVudFRpbWUpICogMTAwMCArIDE1MDtcbiAgICBzcGVha2luZ1RpbWVyUmVmLmN1cnJlbnQgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIGlmIChpc0FjdGl2ZVJlZi5jdXJyZW50KSBzZXRTdGF0dXMoJ2xpc3RlbmluZycpO1xuICAgIH0sIHJlbWFpbmluZ01zKTtcbiAgfSwgW10pO1xuXG4gIGNvbnN0IGhhbmRsZUV2ZW50ID0gdXNlQ2FsbGJhY2soKGRhdGE6IFJlY29yZDxzdHJpbmcsIHVua25vd24+KSA9PiB7XG4gICAgY29uc3QgY2IgPSBvcHRzUmVmLmN1cnJlbnQ7XG4gICAgc3dpdGNoIChkYXRhLnR5cGUpIHtcbiAgICAgIGNhc2UgJ3JlYWR5JzpcbiAgICAgICAgc2V0U3RhdHVzKCdsaXN0ZW5pbmcnKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdpbnB1dF90cmFuc2NyaXB0JzpcbiAgICAgICAgaWYgKHR5cGVvZiBkYXRhLnRleHQgPT09ICdzdHJpbmcnKSBjYi5vbklucHV0VHJhbnNjcmlwdD8uKGRhdGEudGV4dCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnb3V0cHV0X3RyYW5zY3JpcHQnOlxuICAgICAgICBpZiAodHlwZW9mIGRhdGEudGV4dCA9PT0gJ3N0cmluZycpIGNiLm9uT3V0cHV0VHJhbnNjcmlwdD8uKGRhdGEudGV4dCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnaW50ZXJydXB0ZWQnOlxuICAgICAgICBmbHVzaFBsYXliYWNrKCk7XG4gICAgICAgIHNldFN0YXR1cygnbGlzdGVuaW5nJyk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAndHVybl9jb21wbGV0ZSc6XG4gICAgICAgIGNiLm9uVHVybkNvbXBsZXRlPy4oe1xuICAgICAgICAgIHRyYW5zY3JpcHQ6IFN0cmluZyhkYXRhLnRyYW5zY3JpcHQgPz8gJycpLFxuICAgICAgICAgIHJlc3BvbnNlOiBTdHJpbmcoZGF0YS5yZXNwb25zZSA/PyAnJyksXG4gICAgICAgIH0pO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ2Vycm9yJzpcbiAgICAgICAgY2Iub25FcnJvcj8uKG5ldyBFcnJvcihTdHJpbmcoZGF0YS5tZXNzYWdlID8/ICdMaXZlIHZvaWNlIGVycm9yJykpKTtcbiAgICAgICAgc2V0U3RhdHVzKCdlcnJvcicpO1xuICAgICAgICBicmVhaztcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgfSwgW2ZsdXNoUGxheWJhY2tdKTtcblxuICBjb25zdCBzdGFydCA9IHVzZUNhbGxiYWNrKGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBjYiA9IG9wdHNSZWYuY3VycmVudDtcbiAgICBpZiAoIWNiLmFnZW50SWQpIHtcbiAgICAgIGNiLm9uRXJyb3I/LihuZXcgRXJyb3IoJ0xpdmUgdm9pY2UgdW5hdmFpbGFibGU6IGFnZW50IGlzIHN0aWxsIGxvYWRpbmcnKSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICh3c1JlZi5jdXJyZW50KSByZXR1cm47IC8vIGFscmVhZHkgYWN0aXZlXG5cbiAgICBzZXRTdGF0dXMoJ2Nvbm5lY3RpbmcnKTtcbiAgICAvLyBQZXJzaXN0ZW5jZSAodHJhbnNjcmlwdC9kYXNoYm9hcmQpIGtleXMgb2ZmIGFuIGV4aXN0aW5nIGNvbnZlcnNhdGlvbiBVVUlELlxuICAgIC8vIFdpdGhvdXQgb25lIHRoZSBjYWxsIHN0aWxsIHdvcmtzLCBidXQgdGhlIGJhY2tlbmQgc2tpcHMgc2F2aW5nIHRoZSB0dXJucy5cbiAgICBpZiAoIWNiLmNvbnZlcnNhdGlvbklkKSB7XG4gICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICdbdXNlTGl2ZVZvaWNlXSBObyBjb252ZXJzYXRpb25JZCB5ZXQg4oCUIHRoZSBsaXZlIGNhbGwgd2lsbCBydW4gYnV0IGl0cyAnICtcbiAgICAgICAgICAndHJhbnNjcmlwdHMgd2lsbCBub3QgYmUgcGVyc2lzdGVkLicsXG4gICAgICApO1xuICAgIH1cbiAgICBjb25zdCB0aHJlYWRJZCA9XG4gICAgICBjYi5jb252ZXJzYXRpb25JZCB8fFxuICAgICAgKHR5cGVvZiBjcnlwdG8gIT09ICd1bmRlZmluZWQnICYmICdyYW5kb21VVUlEJyBpbiBjcnlwdG9cbiAgICAgICAgPyBjcnlwdG8ucmFuZG9tVVVJRCgpXG4gICAgICAgIDogYGxpdmUtJHtEYXRlLm5vdygpfWApO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHN0cmVhbSA9IGF3YWl0IG5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0VXNlck1lZGlhKHtcbiAgICAgICAgYXVkaW86IHsgY2hhbm5lbENvdW50OiAxLCBlY2hvQ2FuY2VsbGF0aW9uOiB0cnVlLCBub2lzZVN1cHByZXNzaW9uOiB0cnVlIH0sXG4gICAgICB9KTtcbiAgICAgIHN0cmVhbVJlZi5jdXJyZW50ID0gc3RyZWFtO1xuXG4gICAgICBjb25zdCBBdWRpb0N0eCA9XG4gICAgICAgIHdpbmRvdy5BdWRpb0NvbnRleHQgfHxcbiAgICAgICAgKHdpbmRvdyBhcyB1bmtub3duIGFzIHsgd2Via2l0QXVkaW9Db250ZXh0OiB0eXBlb2YgQXVkaW9Db250ZXh0IH0pLndlYmtpdEF1ZGlvQ29udGV4dDtcbiAgICAgIGNvbnN0IGNhcHR1cmVDdHggPSBuZXcgQXVkaW9DdHgoKTtcbiAgICAgIGNhcHR1cmVDdHhSZWYuY3VycmVudCA9IGNhcHR1cmVDdHg7XG4gICAgICBwbGF5YmFja0N0eFJlZi5jdXJyZW50ID0gbmV3IEF1ZGlvQ3R4KCk7XG4gICAgICBwbGF5aGVhZFJlZi5jdXJyZW50ID0gcGxheWJhY2tDdHhSZWYuY3VycmVudC5jdXJyZW50VGltZTtcblxuICAgICAgY29uc3Qgd3MgPSBjcmVhdGVXZWJTb2NrZXQoYnVpbGRMaXZlVXJsKGNiLCB0aHJlYWRJZCkpO1xuICAgICAgd3MuYmluYXJ5VHlwZSA9ICdhcnJheWJ1ZmZlcic7XG4gICAgICB3c1JlZi5jdXJyZW50ID0gd3M7XG5cbiAgICAgIHdzLm9ub3BlbiA9ICgpID0+IHtcbiAgICAgICAgY29uc3Qgc291cmNlID0gY2FwdHVyZUN0eC5jcmVhdGVNZWRpYVN0cmVhbVNvdXJjZShzdHJlYW0pO1xuICAgICAgICAvLyBUT0RPOiBtaWdyYXRlIHRvIEF1ZGlvV29ya2xldCDigJQgU2NyaXB0UHJvY2Vzc29yTm9kZSBpcyBkZXByZWNhdGVkIGFuZFxuICAgICAgICAvLyBydW5zIHRoZSBkb3duc2FtcGxlIG9uIHRoZSBtYWluIHRocmVhZCAoY2FuIGphbmsgb24gc2xvdyBkZXZpY2VzKS5cbiAgICAgICAgY29uc3QgcHJvY2Vzc29yID0gY2FwdHVyZUN0eC5jcmVhdGVTY3JpcHRQcm9jZXNzb3IoQ0FQVFVSRV9CVUZGRVJfU0laRSwgMSwgMSk7XG4gICAgICAgIHByb2Nlc3NvclJlZi5jdXJyZW50ID0gcHJvY2Vzc29yO1xuICAgICAgICBwcm9jZXNzb3Iub25hdWRpb3Byb2Nlc3MgPSAoZSkgPT4ge1xuICAgICAgICAgIGlmICh3cy5yZWFkeVN0YXRlICE9PSB3cy5PUEVOKSByZXR1cm47XG4gICAgICAgICAgY29uc3QgcGNtID0gZmxvYXRUbzE2a1BjbShlLmlucHV0QnVmZmVyLmdldENoYW5uZWxEYXRhKDApLCBjYXB0dXJlQ3R4LnNhbXBsZVJhdGUpO1xuICAgICAgICAgIHdzLnNlbmQocGNtKTtcbiAgICAgICAgfTtcbiAgICAgICAgc291cmNlLmNvbm5lY3QocHJvY2Vzc29yKTtcbiAgICAgICAgcHJvY2Vzc29yLmNvbm5lY3QoY2FwdHVyZUN0eC5kZXN0aW5hdGlvbik7IC8vIHJlcXVpcmVkIHRvIGRyaXZlIG9uYXVkaW9wcm9jZXNzXG4gICAgICAgIHNldElzQWN0aXZlKHRydWUpO1xuICAgICAgICBzZXRTdGF0dXMoJ2xpc3RlbmluZycpO1xuICAgICAgfTtcblxuICAgICAgd3Mub25tZXNzYWdlID0gKGV2ZW50OiBNZXNzYWdlRXZlbnQpID0+IHtcbiAgICAgICAgaWYgKGV2ZW50LmRhdGEgaW5zdGFuY2VvZiBBcnJheUJ1ZmZlcikge1xuICAgICAgICAgIHBsYXlQY21DaHVuayhldmVudC5kYXRhKTtcbiAgICAgICAgfSBlbHNlIGlmICh0eXBlb2YgZXZlbnQuZGF0YSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgaGFuZGxlRXZlbnQoSlNPTi5wYXJzZShldmVudC5kYXRhKSk7XG4gICAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgICAvKiBpZ25vcmUgbWFsZm9ybWVkIGV2ZW50ICovXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9O1xuXG4gICAgICB3cy5vbmVycm9yID0gKCkgPT4ge1xuICAgICAgICBjYi5vbkVycm9yPy4obmV3IEVycm9yKCdMaXZlIHZvaWNlIGNvbm5lY3Rpb24gZXJyb3InKSk7XG4gICAgICB9O1xuXG4gICAgICB3cy5vbmNsb3NlID0gKCkgPT4ge1xuICAgICAgICBzdG9wKCk7XG4gICAgICB9O1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgY2Iub25FcnJvcj8uKGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyIDogbmV3IEVycm9yKFN0cmluZyhlcnIpKSk7XG4gICAgICBzdG9wKCk7XG4gICAgfVxuICB9LCBbaGFuZGxlRXZlbnQsIHBsYXlQY21DaHVuaywgc3RvcF0pO1xuXG4gIC8vIFRlYXIgZG93biBvbiB1bm1vdW50LlxuICB1c2VFZmZlY3QoKCkgPT4gKCkgPT4gc3RvcCgpLCBbc3RvcF0pO1xuXG4gIHJldHVybiB7IGlzQWN0aXZlLCBzdGF0dXMsIHN0YXJ0LCBzdG9wIH07XG59XG4iXX0=
|