1 | ;
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.logUrl = exports.googlePacker = exports.cleanup = exports.getRequestSubscription = exports.getResponseSubscription = exports.getResponseQueueTopic = exports.initialize = exports.initializeGoogleServices = exports.GoogleImpl = exports.defaults = exports.defaultGcWorker = exports.GoogleMetrics = void 0;
|
4 | const abort_controller_1 = require("abort-controller");
|
5 | const gaxios_1 = require("gaxios");
|
6 | const googleapis_1 = require("googleapis");
|
7 | const https = require("https");
|
8 | const util = require("util");
|
9 | const cache_1 = require("../cache");
|
10 | const cost_1 = require("../cost");
|
11 | const error_1 = require("../error");
|
12 | const log_1 = require("../log");
|
13 | const packer_1 = require("../packer");
|
14 | const provider_1 = require("../provider");
|
15 | const serialize_1 = require("../serialize");
|
16 | const shared_1 = require("../shared");
|
17 | const throttle_1 = require("../throttle");
|
18 | const google_queue_1 = require("./google-queue");
|
19 | const google_shared_1 = require("./google-shared");
|
20 | const googleTrampolineHttps = require("./google-trampoline-https");
|
21 | const googleTrampolineQueue = require("./google-trampoline-queue");
|
22 | const gaxios = new gaxios_1.Gaxios({
|
23 | retryConfig: {
|
24 | retry: 3,
|
25 | noResponseRetries: 3,
|
26 | shouldRetry: (0, google_shared_1.shouldRetryRequest)(log_1.log.info)
|
27 | }
|
28 | });
|
29 | const GoogleCloudFunctionsMemorySizes = [128, 256, 512, 1024, 2048];
|
30 | class GoogleMetrics {
|
31 | constructor() {
|
32 | this.outboundBytes = 0;
|
33 | this.pubSubBytes = 0;
|
34 | }
|
35 | }
|
36 | exports.GoogleMetrics = GoogleMetrics;
|
37 | function defaultGcWorker(resources, services) {
|
38 | return deleteResources(services, resources, log_1.log.gc);
|
39 | }
|
40 | exports.defaultGcWorker = defaultGcWorker;
|
41 | exports.defaults = {
|
42 | ...provider_1.commonDefaults,
|
43 | region: "us-central1",
|
44 | googleCloudFunctionOptions: {},
|
45 | _gcWorker: defaultGcWorker
|
46 | };
|
47 | exports.GoogleImpl = {
|
48 | name: "google",
|
49 | initialize,
|
50 | defaults: exports.defaults,
|
51 | cleanup,
|
52 | costSnapshot,
|
53 | logUrl,
|
54 | invoke,
|
55 | poll,
|
56 | responseQueueId
|
57 | };
|
58 | async function initializeGoogleServices() {
|
59 | const auth = await googleapis_1.google.auth.getClient({
|
60 | scopes: ["https://www.googleapis.com/auth/cloud-platform"]
|
61 | });
|
62 | googleapis_1.google.options({
|
63 | auth,
|
64 | retryConfig: {
|
65 | retry: 8,
|
66 | retryDelay: 250,
|
67 | noResponseRetries: 3,
|
68 | shouldRetry: (0, google_shared_1.shouldRetryRequest)(log_1.log.info)
|
69 | }
|
70 | });
|
71 | return {
|
72 | cloudFunctions: googleapis_1.google.cloudfunctions("v1"),
|
73 | pubsub: googleapis_1.google.pubsub("v1"),
|
74 | cloudBilling: googleapis_1.google.cloudbilling("v1"),
|
75 | google: googleapis_1.google
|
76 | };
|
77 | }
|
78 | exports.initializeGoogleServices = initializeGoogleServices;
|
79 | async function defaultPollDelay(retries) {
|
80 | if (retries > 5) {
|
81 | await (0, shared_1.sleep)(5 * 1000);
|
82 | }
|
83 | await (0, shared_1.sleep)((retries + 1) * 500);
|
84 | }
|
85 | async function pollOperation({ request, checkDone, delay = defaultPollDelay, maxRetries = 50 }) {
|
86 | let retries = 0;
|
87 | await delay(retries);
|
88 | while (true) {
|
89 | log_1.log.info(`Polling...`);
|
90 | const result = await request();
|
91 | if (checkDone(result)) {
|
92 | log_1.log.info(`Done.`);
|
93 | return result;
|
94 | }
|
95 | if (retries++ >= maxRetries) {
|
96 | throw new error_1.FaastError(`Timed out after ${retries} attempts.`);
|
97 | }
|
98 | await delay(retries);
|
99 | }
|
100 | }
|
101 | async function quietly(promise) {
|
102 | try {
|
103 | const result = await promise;
|
104 | return result.data;
|
105 | }
|
106 | catch (err) {
|
107 | return;
|
108 | }
|
109 | }
|
110 | const throttleGoogleWrite = (0, throttle_1.throttle)({
|
111 | concurrency: 4,
|
112 | rate: 3,
|
113 | retry: (err, n) => {
|
114 | const { message } = err;
|
115 | return (n < 6 &&
|
116 | (message.match(/Build failed/) !== null ||
|
117 | message.match(/Quota/) !== null ||
|
118 | message.match(/load attempt timed out/) !== null ||
|
119 | message.match(/ECONNRESET/) !== null ||
|
120 | message.match(/failed on loading user code/) != null));
|
121 | }
|
122 | }, (op) => op());
|
123 | async function waitFor(api, response) {
|
124 | return throttleGoogleWrite(async () => {
|
125 | let operation;
|
126 | try {
|
127 | operation = await response();
|
128 | }
|
129 | catch (err) {
|
130 | throw new error_1.FaastError(err, "could not get operation");
|
131 | }
|
132 | const operationName = operation.data.name;
|
133 | try {
|
134 | return pollOperation({
|
135 | request: () => quietly(api.operations.get({ name: operationName })),
|
136 | checkDone: result => {
|
137 | /* istanbul ignore if */
|
138 | if (!result) {
|
139 | return false;
|
140 | }
|
141 | /* istanbul ignore if */
|
142 | if (result.error) {
|
143 | const underlying = new error_1.FaastError(result.error.message ?? undefined);
|
144 | underlying.stack = "";
|
145 | throw new error_1.FaastError(underlying, "Error polling operation");
|
146 | }
|
147 | return result.done || false;
|
148 | }
|
149 | });
|
150 | }
|
151 | catch (err) {
|
152 | throw new error_1.FaastError(err, "poll operation failed");
|
153 | }
|
154 | });
|
155 | }
|
156 | async function deleteFunction(api, path) {
|
157 | try {
|
158 | return await waitFor(api, () => api.projects.locations.functions.delete({
|
159 | name: path
|
160 | }));
|
161 | }
|
162 | catch (err) {
|
163 | if (err.message.match(/does not exist/)) {
|
164 | return;
|
165 | }
|
166 | throw err;
|
167 | }
|
168 | }
|
169 | async function initialize(fmodule, nonce, options) {
|
170 | log_1.log.info(`Create google cloud function`);
|
171 | const services = await initializeGoogleServices();
|
172 | const project = await googleapis_1.google.auth.getProjectId();
|
173 | const { cloudFunctions, pubsub } = services;
|
174 | const { region } = options;
|
175 | const location = `projects/${project}/locations/${region}`;
|
176 | const functionName = "faast-" + nonce;
|
177 | const { timeout } = options;
|
178 | const { wrapperVerbose } = options.debugOptions;
|
179 | async function createCodeBundle() {
|
180 | const wrapperOptions = {
|
181 | childProcessTimeoutMs: Math.max(1000, (timeout - 5) * 1000),
|
182 | wrapperVerbose
|
183 | };
|
184 | const { archive } = await googlePacker(fmodule, options, wrapperOptions, functionName);
|
185 | const uploadUrlResponse = await throttleGoogleWrite(() => cloudFunctions.projects.locations.functions.generateUploadUrl({
|
186 | parent: location
|
187 | }));
|
188 | const uploadResult = await uploadZip(uploadUrlResponse.data.uploadUrl, archive);
|
189 | log_1.log.info(`Upload zip file response: ${uploadResult?.statusText}`);
|
190 | return uploadUrlResponse.data.uploadUrl;
|
191 | }
|
192 | const trampoline = `projects/${project}/locations/${region}/functions/${functionName}`;
|
193 | const resources = {
|
194 | trampoline,
|
195 | region
|
196 | };
|
197 | const state = {
|
198 | resources,
|
199 | services,
|
200 | project,
|
201 | functionName,
|
202 | metrics: new GoogleMetrics(),
|
203 | options
|
204 | };
|
205 | const { gc, retentionInDays, _gcWorker: gcWorker } = options;
|
206 | if (gc === "auto" || gc === "force") {
|
207 | log_1.log.gc(`Starting garbage collector`);
|
208 | state.gcPromise = collectGarbage(gcWorker, services, project, retentionInDays).catch(err => {
|
209 | log_1.log.gc(`Garbage collection error: ${err}`);
|
210 | });
|
211 | }
|
212 | const pricingPromise = getGoogleCloudFunctionsPricing(services.cloudBilling, region);
|
213 | const { mode } = options;
|
214 | const responseQueuePromise = (async () => {
|
215 | const topic = await pubsub.projects.topics.create({
|
216 | name: getResponseQueueTopic(project, functionName)
|
217 | });
|
218 | resources.responseQueueTopic = topic.data.name ?? undefined;
|
219 | resources.responseSubscription = getResponseSubscription(project, functionName);
|
220 | log_1.log.info(`Creating response queue subscription`);
|
221 | await pubsub.projects.subscriptions.create({
|
222 | name: resources.responseSubscription,
|
223 | requestBody: {
|
224 | topic: resources.responseQueueTopic
|
225 | }
|
226 | });
|
227 | })();
|
228 | let requestQueuePromise;
|
229 | if (mode === "queue") {
|
230 | log_1.log.info(`Initializing queue`);
|
231 | resources.requestQueueTopic = getRequestQueueTopic(project, functionName);
|
232 | requestQueuePromise = pubsub.projects.topics.create({
|
233 | name: resources.requestQueueTopic
|
234 | });
|
235 | resources.requestSubscription = getRequestSubscription(project, functionName, region);
|
236 | }
|
237 | const sourceUploadUrl = await createCodeBundle();
|
238 | const { memorySize, googleCloudFunctionOptions, env } = options;
|
239 | if (!GoogleCloudFunctionsMemorySizes.find(size => size === memorySize)) {
|
240 | log_1.log.warn(`Invalid memorySize ${memorySize} for Google Cloud Functions`);
|
241 | }
|
242 | const requestBody = {
|
243 | name: trampoline,
|
244 | entryPoint: "trampoline",
|
245 | timeout: `${timeout}s`,
|
246 | availableMemoryMb: memorySize,
|
247 | sourceUploadUrl,
|
248 | environmentVariables: env,
|
249 | runtime: "nodejs14",
|
250 | ...googleCloudFunctionOptions
|
251 | };
|
252 | if (mode === "queue") {
|
253 | await requestQueuePromise;
|
254 | requestBody.eventTrigger = {
|
255 | eventType: "providers/cloud.pubsub/eventTypes/topic.publish",
|
256 | resource: resources.requestQueueTopic
|
257 | };
|
258 | }
|
259 | else {
|
260 | requestBody.httpsTrigger = {};
|
261 | }
|
262 | log_1.log.info(`Create function at ${location}`);
|
263 | log_1.log.info(`Request body: %O`, requestBody);
|
264 | await (0, throttle_1.retryOp)(3, async () => {
|
265 | try {
|
266 | log_1.log.info(`create function ${requestBody.name} [${options.description}]`);
|
267 | await waitFor(cloudFunctions, () => cloudFunctions.projects.locations.functions.create({
|
268 | location,
|
269 | requestBody
|
270 | }));
|
271 | await cloudFunctions.projects.locations.functions.setIamPolicy({
|
272 | resource: trampoline,
|
273 | requestBody: {
|
274 | policy: {
|
275 | bindings: [
|
276 | {
|
277 | members: ["allUsers"],
|
278 | role: "roles/cloudfunctions.invoker"
|
279 | }
|
280 | ]
|
281 | }
|
282 | }
|
283 | });
|
284 | }
|
285 | catch (err) {
|
286 | /* istanbul ignore next */
|
287 | await deleteFunction(cloudFunctions, trampoline).catch(() => { });
|
288 | throw new error_1.FaastError({ cause: err, name: error_1.FaastErrorNames.ECREATE }, "failed to create google cloud function");
|
289 | }
|
290 | });
|
291 | if (mode === "https" || mode === "auto") {
|
292 | try {
|
293 | const func = await cloudFunctions.projects.locations.functions.get({
|
294 | name: trampoline
|
295 | });
|
296 | if (!func.data.httpsTrigger) {
|
297 | throw new error_1.FaastError("Could not get http trigger url");
|
298 | }
|
299 | const { url } = func.data.httpsTrigger;
|
300 | if (!url) {
|
301 | throw new error_1.FaastError("Could not get http trigger url");
|
302 | }
|
303 | log_1.log.info(`Function URL: ${url}`);
|
304 | state.url = url;
|
305 | }
|
306 | catch (err) {
|
307 | throw new error_1.FaastError(err, `Could not get function ${trampoline} or its url, despite it being created`);
|
308 | }
|
309 | }
|
310 | await pricingPromise;
|
311 | await responseQueuePromise;
|
312 | return state;
|
313 | }
|
314 | exports.initialize = initialize;
|
315 | function getRequestQueueTopic(project, functionName) {
|
316 | return `projects/${project}/topics/${functionName}-Requests`;
|
317 | }
|
318 | function getResponseQueueTopic(project, functionName) {
|
319 | return `projects/${project}/topics/${functionName}-Responses`;
|
320 | }
|
321 | exports.getResponseQueueTopic = getResponseQueueTopic;
|
322 | function getResponseSubscription(project, functionName) {
|
323 | return `projects/${project}/subscriptions/${functionName}-Responses`;
|
324 | }
|
325 | exports.getResponseSubscription = getResponseSubscription;
|
326 | function getRequestSubscription(project, functionName, region) {
|
327 | return `projects/${project}/subscriptions/gcf-${functionName}-${region}-${functionName}-Requests`;
|
328 | }
|
329 | exports.getRequestSubscription = getRequestSubscription;
|
330 | const agent = new https.Agent({ keepAlive: true, timeout: 0, maxSockets: 1000 });
|
331 | async function callFunctionHttps(url, call, metrics, cancel) {
|
332 | const source = new abort_controller_1.AbortController();
|
333 | try {
|
334 | const axiosConfig = {
|
335 | method: "POST",
|
336 | url,
|
337 | headers: { "Content-Type": "application/json" },
|
338 | body: (0, serialize_1.serialize)(call),
|
339 | signal: source.signal,
|
340 | responseType: "json",
|
341 | retry: false,
|
342 | agent
|
343 | };
|
344 | const rawResponse = await Promise.race([
|
345 | gaxios.request(axiosConfig),
|
346 | cancel
|
347 | ]);
|
348 | if (!rawResponse) {
|
349 | log_1.log.info(`cancelling gcp invoke`);
|
350 | source.abort();
|
351 | return;
|
352 | }
|
353 | try {
|
354 | metrics.outboundBytes += (0, shared_1.computeHttpResponseBytes)(rawResponse.headers);
|
355 | }
|
356 | catch (err) {
|
357 | throw new error_1.FaastError(err, `Could not parse ${util.inspect(rawResponse.data)}`);
|
358 | }
|
359 | }
|
360 | catch (err) {
|
361 | const { response } = err;
|
362 | if (response) {
|
363 | if (response.status === 503) {
|
364 | throw new error_1.FaastError({ cause: err, name: error_1.FaastErrorNames.EMEMORY }, "google cloud function: possibly out of memory");
|
365 | }
|
366 | throw new error_1.FaastError(err, `when invoking google cloud function: %s\nDetails: %s`, response.statusText, response.data);
|
367 | }
|
368 | throw new error_1.FaastError(err, `when invoking google cloud function`);
|
369 | }
|
370 | }
|
371 | async function invoke(state, call, cancel) {
|
372 | const { options, resources, services, url, metrics } = state;
|
373 | switch (options.mode) {
|
374 | case "auto":
|
375 | case "https":
|
376 | return callFunctionHttps(url, call, metrics, cancel);
|
377 | case "queue":
|
378 | const { requestQueueTopic } = resources;
|
379 | const { pubsub } = services;
|
380 | const serialized = (0, serialize_1.serialize)(call);
|
381 | return (0, google_queue_1.publishPubSub)(pubsub, requestQueueTopic, serialized);
|
382 | }
|
383 | }
|
384 | function poll(state, cancel) {
|
385 | return (0, google_queue_1.receiveMessages)(state.services.pubsub, state.resources.responseSubscription, state.metrics, cancel);
|
386 | }
|
387 | function responseQueueId(state) {
|
388 | return state.resources.responseQueueTopic;
|
389 | }
|
390 | async function deleteResources(services, resources, output = log_1.log.info) {
|
391 | const { trampoline, requestQueueTopic, requestSubscription, responseSubscription, responseQueueTopic, region, ...rest } = resources;
|
392 | const _exhaustiveCheck = {};
|
393 | const { cloudFunctions, pubsub } = services;
|
394 | // We deliberately rethrow transient errors here, so only if all prior
|
395 | // deletes succeed do we proceed. If there's a transient error then future
|
396 | // garbage collection will clean up. The order is important; the function
|
397 | // itself must be deleted last.
|
398 | const check = async (request) => {
|
399 | try {
|
400 | await request;
|
401 | }
|
402 | catch (err) {
|
403 | /* istanbul ignore next */
|
404 | if (err.message.match(/Resource not found/)) {
|
405 | return;
|
406 | }
|
407 | throw err;
|
408 | }
|
409 | };
|
410 | if (responseSubscription) {
|
411 | await check(pubsub.projects.subscriptions.delete({ subscription: responseSubscription }));
|
412 | output(`Deleted response subscription: ${responseSubscription}`);
|
413 | }
|
414 | if (responseQueueTopic) {
|
415 | await check(pubsub.projects.topics.delete({ topic: responseQueueTopic }));
|
416 | output(`Deleted response queue topic: ${responseQueueTopic}`);
|
417 | }
|
418 | if (requestSubscription) {
|
419 | await check(pubsub.projects.subscriptions.delete({ subscription: requestSubscription }));
|
420 | output(`Deleted response subscription: ${requestSubscription}`);
|
421 | }
|
422 | if (requestQueueTopic) {
|
423 | await check(pubsub.projects.topics.delete({ topic: requestQueueTopic }));
|
424 | output(`Deleted request queue topic: ${requestQueueTopic}`);
|
425 | }
|
426 | if (trampoline) {
|
427 | await check(deleteFunction(cloudFunctions, trampoline));
|
428 | output(`Deleted function ${trampoline}`);
|
429 | }
|
430 | }
|
431 | async function cleanup(state, options) {
|
432 | log_1.log.info(`google cleanup starting.`);
|
433 | if (state.gcPromise) {
|
434 | log_1.log.info(`Waiting for garbage collection...`);
|
435 | await state.gcPromise;
|
436 | log_1.log.info(`Garbage collection done.`);
|
437 | }
|
438 | if (options.deleteResources) {
|
439 | try {
|
440 | await deleteResources(state.services, state.resources);
|
441 | }
|
442 | catch (err) {
|
443 | throw new error_1.FaastError(err, "delete resources failed");
|
444 | }
|
445 | }
|
446 | log_1.log.info(`google cleanup done.`);
|
447 | }
|
448 | exports.cleanup = cleanup;
|
449 | let garbageCollectorRunning = false;
|
450 | async function collectGarbage(gcWorker, services, proj, retentionInDays) {
|
451 | if (gcWorker === defaultGcWorker) {
|
452 | if (garbageCollectorRunning) {
|
453 | return;
|
454 | }
|
455 | garbageCollectorRunning = true;
|
456 | }
|
457 | try {
|
458 | const { cloudFunctions } = services;
|
459 | let pageToken;
|
460 | let promises = [];
|
461 | const scheduleDeleteResources = (0, throttle_1.throttle)({
|
462 | concurrency: 5,
|
463 | rate: 5,
|
464 | burst: 2
|
465 | }, async (gServices, fn) => {
|
466 | const { region, name, project } = parseFunctionName(fn.name);
|
467 | const resources = {
|
468 | region,
|
469 | trampoline: fn.name,
|
470 | requestQueueTopic: getRequestQueueTopic(project, name),
|
471 | requestSubscription: getRequestSubscription(project, name, region),
|
472 | responseQueueTopic: getResponseQueueTopic(project, name),
|
473 | responseSubscription: getResponseSubscription(project, name)
|
474 | };
|
475 | await gcWorker(resources, gServices);
|
476 | });
|
477 | const fnPattern = new RegExp(`/functions/faast-${shared_1.uuidv4Pattern}$`);
|
478 | do {
|
479 | const funcListResponse = await cloudFunctions.projects.locations.functions.list({
|
480 | parent: `projects/${proj}/locations/-`,
|
481 | pageToken
|
482 | });
|
483 | pageToken = funcListResponse.data.nextPageToken ?? undefined;
|
484 | const garbageFunctions = (funcListResponse.data.functions || [])
|
485 | .filter(fn => (0, shared_1.hasExpired)(fn.updateTime, retentionInDays))
|
486 | .filter(fn => fn.name.match(fnPattern));
|
487 | promises = garbageFunctions.map(fn => scheduleDeleteResources(services, fn));
|
488 | } while (pageToken);
|
489 | await Promise.all(promises);
|
490 | }
|
491 | finally {
|
492 | if (gcWorker === defaultGcWorker) {
|
493 | garbageCollectorRunning = false;
|
494 | }
|
495 | }
|
496 | }
|
497 | function parseFunctionName(path) {
|
498 | const match = path.match(/^projects\/(.*)\/locations\/(.*)\/functions\/(.*)$/);
|
499 | return match && { project: match[1], region: match[2], name: match[3] };
|
500 | }
|
501 | async function uploadZip(url, zipStream) {
|
502 | const config = {
|
503 | method: "PUT",
|
504 | url,
|
505 | body: zipStream,
|
506 | headers: {
|
507 | "content-type": "application/zip",
|
508 | "x-goog-content-length-range": "0,104857600"
|
509 | }
|
510 | };
|
511 | return gaxios.request(config);
|
512 | }
|
513 | async function googlePacker(functionModule, options, wrapperOptions, FunctionName) {
|
514 | const { mode } = options;
|
515 | const trampolineModule = mode === "queue" ? googleTrampolineQueue : googleTrampolineHttps;
|
516 | return (0, packer_1.packer)(trampolineModule, functionModule, options, wrapperOptions, FunctionName);
|
517 | }
|
518 | exports.googlePacker = googlePacker;
|
519 | let getGooglePrice;
|
520 | function ensureGooglePriceCache(cloudBilling) {
|
521 | if (getGooglePrice) {
|
522 | return;
|
523 | }
|
524 | getGooglePrice = (0, throttle_1.throttle)({
|
525 | concurrency: 1,
|
526 | rate: 3,
|
527 | memoize: true,
|
528 | cache: cache_1.caches.googlePrices
|
529 | }, async (region, serviceName, description, conversionFactor) => {
|
530 | try {
|
531 | const skusResponse = await cloudBilling.services.skus.list({
|
532 | parent: serviceName
|
533 | });
|
534 | const { skus = [] } = skusResponse.data;
|
535 | const matchingSkus = skus.filter(sku => sku.description === description);
|
536 | log_1.log.provider(`matching SKUs: ${util.inspect(matchingSkus, { depth: null })}`);
|
537 | const regionOrGlobalSku = matchingSkus.find(sku => sku.serviceRegions.find(r => r === region)) ??
|
538 | matchingSkus.find(sku => sku.serviceRegions.find(r => r === "global"));
|
539 | const pexp = regionOrGlobalSku.pricingInfo[0].pricingExpression;
|
540 | const prices = pexp.tieredRates.map(rate => Number(rate.unitPrice.units ?? "0") +
|
541 | rate.unitPrice.nanos / 1e9);
|
542 | const price = Math.max(...prices) *
|
543 | (conversionFactor / pexp.baseUnitConversionFactor);
|
544 | log_1.log.provider(`Found price for ${serviceName}, ${description}, ${region}: ${price}`);
|
545 | return price;
|
546 | }
|
547 | catch (err) {
|
548 | throw new error_1.FaastError(err, `failed to get google pricing for "${description}"`);
|
549 | }
|
550 | });
|
551 | }
|
552 | let googleServices;
|
553 | const listGoogleServices = (0, throttle_1.throttle)({ concurrency: 1 }, async (cloudBilling) => {
|
554 | if (googleServices) {
|
555 | return googleServices;
|
556 | }
|
557 | const response = await cloudBilling.services.list();
|
558 | googleServices = response.data.services;
|
559 | return googleServices;
|
560 | });
|
561 | async function getGoogleCloudFunctionsPricing(cloudBilling, region) {
|
562 | const services = await listGoogleServices(cloudBilling);
|
563 | ensureGooglePriceCache(cloudBilling);
|
564 | const getPricing = (serviceName, description, conversionFactor = 1) => {
|
565 | const service = services.find(s => s.displayName === serviceName);
|
566 | return getGooglePrice(region, service.name, description, conversionFactor);
|
567 | };
|
568 | return {
|
569 | perInvocation: await getPricing("Cloud Functions", "Invocations"),
|
570 | perGhzSecond: await getPricing("Cloud Functions", "CPU Time"),
|
571 | perGbSecond: await getPricing("Cloud Functions", "Memory Time", 2 ** 30),
|
572 | perGbOutboundData: await getPricing("Cloud Functions", `Network Egress from ${region}`, 2 ** 30),
|
573 | perGbPubSub: await getPricing("Cloud Pub/Sub", "Message Delivery Basic", 2 ** 30)
|
574 | };
|
575 | }
|
576 | // https://cloud.google.com/functions/pricing
|
577 | const gcfProvisonableMemoryTable = {
|
578 | 128: 0.2,
|
579 | 256: 0.4,
|
580 | 512: 0.8,
|
581 | 1024: 1.4,
|
582 | 2048: 2.4
|
583 | };
|
584 | async function costSnapshot(state, stats) {
|
585 | const costs = new cost_1.CostSnapshot("google", state.options, stats);
|
586 | const { memorySize = exports.defaults.memorySize } = state.options;
|
587 | const provisionableSizes = (0, shared_1.keysOf)(gcfProvisonableMemoryTable)
|
588 | .map(n => Number(n))
|
589 | .sort((a, b) => a - b);
|
590 | const provisionedMb = provisionableSizes.find(size => memorySize <= size);
|
591 | if (!provisionedMb) {
|
592 | log_1.log.warn(`Could not determine provisioned memory or CPU for requested memory size ${memorySize}`);
|
593 | }
|
594 | const provisionedGhz = gcfProvisonableMemoryTable[provisionedMb];
|
595 | const billedTimeStats = stats.estimatedBilledTime;
|
596 | const seconds = (billedTimeStats.mean / 1000) * billedTimeStats.samples;
|
597 | const { region } = state.resources;
|
598 | const prices = await getGoogleCloudFunctionsPricing(state.services.cloudBilling, region);
|
599 | const provisionedGb = provisionedMb / 1024;
|
600 | const functionCallDuration = new cost_1.CostMetric({
|
601 | name: "functionCallDuration",
|
602 | pricing: prices.perGbSecond * provisionedGb + prices.perGhzSecond * provisionedGhz,
|
603 | unit: "second",
|
604 | measured: seconds,
|
605 | comment: `https://cloud.google.com/functions/pricing#compute_time (${provisionedMb} MB, ${provisionedGhz} GHz)`
|
606 | });
|
607 | costs.push(functionCallDuration);
|
608 | const functionCallRequests = new cost_1.CostMetric({
|
609 | name: "functionCallRequests",
|
610 | pricing: prices.perInvocation,
|
611 | measured: stats.invocations,
|
612 | unit: "request",
|
613 | comment: "https://cloud.google.com/functions/pricing#invocations"
|
614 | });
|
615 | costs.push(functionCallRequests);
|
616 | const outboundDataTransfer = new cost_1.CostMetric({
|
617 | name: "outboundDataTransfer",
|
618 | pricing: prices.perGbOutboundData,
|
619 | measured: state.metrics.outboundBytes / 2 ** 30,
|
620 | unit: "GB",
|
621 | comment: "https://cloud.google.com/functions/pricing#networking"
|
622 | });
|
623 | costs.push(outboundDataTransfer);
|
624 | const pubsub = new cost_1.CostMetric({
|
625 | name: "pubsub",
|
626 | pricing: prices.perGbPubSub,
|
627 | measured: state.metrics.pubSubBytes / 2 ** 30,
|
628 | unit: "GB",
|
629 | comment: "https://cloud.google.com/pubsub/pricing"
|
630 | });
|
631 | costs.push(pubsub);
|
632 | return costs;
|
633 | }
|
634 | function logUrl(state) {
|
635 | const { project, functionName } = state;
|
636 | return `https://console.cloud.google.com/logs/viewer?project=${project}&resource=cloud_function%2Ffunction_name%2F${functionName}`;
|
637 | }
|
638 | exports.logUrl = logUrl;
|
639 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ29vZ2xlLWZhYXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2dvb2dsZS9nb29nbGUtZmFhc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsdURBQW1EO0FBQ25ELG1DQUE4RTtBQUM5RSwyQ0FNb0I7QUFDcEIsK0JBQStCO0FBQy9CLDZCQUE2QjtBQUM3QixvQ0FBa0M7QUFDbEMsa0NBQW1EO0FBQ25ELG9DQUF1RDtBQUN2RCxnQ0FBNkI7QUFDN0Isc0NBQWlEO0FBQ2pELDBDQVFxQjtBQUNyQiw0Q0FBeUM7QUFDekMsc0NBTW1CO0FBQ25CLDBDQUFnRDtBQUVoRCxpREFBZ0U7QUFDaEUsbURBQXFEO0FBQ3JELG1FQUFtRTtBQUNuRSxtRUFBbUU7QUFNbkUsTUFBTSxNQUFNLEdBQUcsSUFBSSxlQUFNLENBQUM7SUFDdEIsV0FBVyxFQUFFO1FBQ1QsS0FBSyxFQUFFLENBQUM7UUFDUixpQkFBaUIsRUFBRSxDQUFDO1FBQ3BCLFdBQVcsRUFBRSxJQUFBLGtDQUFrQixFQUFDLFNBQUcsQ0FBQyxJQUFJLENBQUM7S0FDNUM7Q0FDSixDQUFDLENBQUM7QUE4QkgsTUFBTSwrQkFBK0IsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztBQXdEcEUsTUFBYSxhQUFhO0lBQTFCO1FBQ0ksa0JBQWEsR0FBRyxDQUFDLENBQUM7UUFDbEIsZ0JBQVcsR0FBRyxDQUFDLENBQUM7SUFDcEIsQ0FBQztDQUFBO0FBSEQsc0NBR0M7QUF1QkQsU0FBZ0IsZUFBZSxDQUFDLFNBQTBCLEVBQUUsUUFBd0I7SUFDaEYsT0FBTyxlQUFlLENBQUMsUUFBUSxFQUFFLFNBQVMsRUFBRSxTQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7QUFDeEQsQ0FBQztBQUZELDBDQUVDO0FBRVksUUFBQSxRQUFRLEdBQTRCO0lBQzdDLEdBQUcseUJBQWM7SUFDakIsTUFBTSxFQUFFLGFB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QUssQ0FBQztJQUN4QyxPQUFPLHdEQUF3RCxPQUFPLDhDQUE4QyxZQUFZLEVBQUUsQ0FBQztBQUN2SSxDQUFDO0FBSEQsd0JBR0MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBBYm9ydENvbnRyb2xsZXIgfSBmcm9tIFwiYWJvcnQtY29udHJvbGxlclwiO1xuaW1wb3J0IHsgR2F4aW9zLCBHYXhpb3NPcHRpb25zLCBHYXhpb3NQcm9taXNlLCBHYXhpb3NSZXNwb25zZSB9IGZyb20gXCJnYXhpb3NcIjtcbmltcG9ydCB7XG4gICAgY2xvdWRiaWxsaW5nX3YxLFxuICAgIGNsb3VkZnVuY3Rpb25zX3YxLFxuICAgIGdvb2dsZSxcbiAgICBHb29nbGVBcGlzLFxuICAgIHB1YnN1Yl92MVxufSBmcm9tIFwiZ29vZ2xlYXBpc1wiO1xuaW1wb3J0ICogYXMgaHR0cHMgZnJvbSBcImh0dHBzXCI7XG5pbXBvcnQgKiBhcyB1dGlsIGZyb20gXCJ1dGlsXCI7XG5pbXBvcnQgeyBjYWNoZXMgfSBmcm9tIFwiLi4vY2FjaGVcIjtcbmltcG9ydCB7IENvc3RNZXRyaWMsIENvc3RTbmFwc2hvdCB9IGZyb20gXCIuLi9jb3N0XCI7XG5pbXBvcnQgeyBGYWFzdEVycm9yLCBGYWFzdEVycm9yTmFtZXMgfSBmcm9tIFwiLi4vZXJyb3JcIjtcbmltcG9ydCB7IGxvZyB9IGZyb20gXCIuLi9sb2dcIjtcbmltcG9ydCB7IHBhY2tlciwgUGFja2VyUmVzdWx0IH0gZnJvbSBcIi4uL3BhY2tlclwiO1xuaW1wb3J0IHtcbiAgICBDbGVhbnVwT3B0aW9ucyxcbiAgICBjb21tb25EZWZhdWx0cyxcbiAgICBDb21tb25PcHRpb25zLFxuICAgIEZ1bmN0aW9uU3RhdHMsXG4gICAgUG9sbFJlc3VsdCxcbiAgICBQcm92aWRlckltcGwsXG4gICAgVVVJRFxufSBmcm9tIFwiLi4vcHJvdmlkZXJcIjtcbmltcG9ydCB7IHNlcmlhbGl6ZSB9IGZyb20gXCIuLi9zZXJpYWxpemVcIjtcbmltcG9ydCB7XG4gICAgY29tcHV0ZUh0dHBSZXNwb25zZUJ5dGVzLFxuICAgIGhhc0V4cGlyZWQsXG4gICAga2V5c09mLFxuICAgIHNsZWVwLFxuICAgIHV1aWR2NFBhdHRlcm5cbn0gZnJvbSBcIi4uL3NoYXJlZFwiO1xuaW1wb3J0IHsgcmV0cnlPcCwgdGhyb3R0bGUgfSBmcm9tIFwiLi4vdGhyb3R0bGVcIjtcbmltcG9ydCB7IEZ1bmN0aW9uQ2FsbCwgV3JhcHBlck9wdGlvbnMgfSBmcm9tIFwiLi4vd3JhcHBlclwiO1xuaW1wb3J0IHsgcHVibGlzaFB1YlN1YiwgcmVjZWl2ZU1lc3NhZ2VzIH0gZnJvbSBcIi4vZ29vZ2xlLXF1ZXVlXCI7XG5pbXBvcnQgeyBzaG91bGRSZXRyeVJlcXVlc3QgfSBmcm9tIFwiLi9nb29nbGUtc2hhcmVkXCI7XG5pbXBvcnQgKiBhcyBnb29nbGVUcmFtcG9saW5lSHR0cHMgZnJvbSBcIi4vZ29vZ2xlLXRyYW1wb2xpbmUtaHR0cHNcIjtcbmltcG9ydCAqIGFzIGdvb2dsZVRyYW1wb2xpbmVRdWV1ZSBmcm9tIFwiLi9nb29nbGUtdHJhbXBvbGluZS1xdWV1ZVwiO1xuXG5pbXBvcnQgQ2xvdWRGdW5jdGlvbnMgPSBjbG91ZGZ1bmN0aW9uc192MTtcbmltcG9ydCBQdWJTdWJBcGkgPSBwdWJzdWJfdjE7XG5pbXBvcnQgQ2xvdWRCaWxsaW5nID0gY2xvdWRiaWxsaW5nX3YxO1xuXG5jb25zdCBnYXhpb3MgPSBuZXcgR2F4aW9zKHtcbiAgICByZXRyeUNvbmZpZzoge1xuICAgICAgICByZXRyeTogMyxcbiAgICAgICAgbm9SZXNwb25zZVJldHJpZXM6IDMsXG4gICAgICAgIHNob3VsZFJldHJ5OiBzaG91bGRSZXRyeVJlcXVlc3QobG9nLmluZm8pXG4gICAgfVxufSk7XG5cbi8qKlxuICogVmFsaWQgR29vZ2xlIENsb3VkXG4gKiB7QGxpbmsgaHR0cHM6Ly9jbG91ZC5nb29nbGUuY29tL2NvbXB1dGUvZG9jcy9yZWdpb25zLXpvbmVzLyB8IHJlZ2lvbnN9LiBPbmx5XG4gKiBzb21lIG9mIHRoZXNlXG4gKiB7QGxpbmsgaHR0cHM6Ly9jbG91ZC5nb29nbGUuY29tL2Z1bmN0aW9ucy9kb2NzL2xvY2F0aW9ucyB8IHJlZ2lvbnMgaGF2ZSBDbG91ZCBGdW5jdGlvbnN9LlxuICogQHB1YmxpY1xuICovXG5leHBvcnQgdHlwZSBHb29nbGVSZWdpb24gPVxuICAgIHwgXCJhc2lhLWVhc3QxXCJcbiAgICB8IFwiYXNpYS1lYXN0MlwiXG4gICAgfCBcImFzaWEtbm9ydGhlYXN0MVwiXG4gICAgfCBcImFzaWEtc291dGgxXCJcbiAgICB8IFwiYXNpYS1zb3V0aGVhc3QxXCJcbiAgICB8IFwiYXVzdHJhbGlhLXNvdXRoZWFzdDFcIlxuICAgIHwgXCJldXJvcGUtbm9ydGgxXCJcbiAgICB8IFwiZXVyb3BlLXdlc3QxXCJcbiAgICB8IFwiZXVyb3BlLXdlc3QyXCJcbiAgICB8IFwiZXVyb3BlLXdlc3QzXCJcbiAgICB8IFwiZXVyb3BlLXdlc3Q0XCJcbiAgICB8IFwiZXVyb3BlLXdlc3Q2XCJcbiAgICB8IFwibm9ydGhhbWVyaWNhLW5vcnRoZWFzdDFcIlxuICAgIHwgXCJzb3V0aGFtZXJpY2EtZWFzdDFcIlxuICAgIHwgXCJ1cy1jZW50cmFsMVwiXG4gICAgfCBcInVzLWVhc3QxXCJcbiAgICB8IFwidXMtZWFzdDRcIlxuICAgIHwgXCJ1cy13ZXN0MVwiXG4gICAgfCBcInVzLXdlc3QyXCI7XG5cbmNvbnN0IEdvb2dsZUNsb3VkRnVuY3Rpb25zTWVtb3J5U2l6ZXMgPSBbMTI4LCAyNTYsIDUxMiwgMTAyNCwgMjA0OF07XG5cbi8qKlxuICogR29vZ2xlLXNwZWNpZmljIG9wdGlvbnMgZm9yIHtAbGluayBmYWFzdEdvb2dsZX0uXG4gKiBAcHVibGljXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgR29vZ2xlT3B0aW9ucyBleHRlbmRzIENvbW1vbk9wdGlvbnMge1xuICAgIC8qKlxuICAgICAqIFRoZSByZWdpb24gdG8gY3JlYXRlIHJlc291cmNlcyBpbi4gR2FyYmFnZSBjb2xsZWN0aW9uIGlzIGFsc28gbGltaXRlZCB0b1xuICAgICAqIHRoaXMgcmVnaW9uLiBEZWZhdWx0OiBgXCJ1cy1jZW50cmFsMVwiYC5cbiAgICAgKi9cbiAgICByZWdpb24/OiBHb29nbGVSZWdpb247XG4gICAgLyoqXG4gICAgICogQWRkaXRpb25hbCBvcHRpb25zIHRvIHBhc3MgdG8gR29vZ2xlIENsb3VkIEZ1bmN0aW9uIGNyZWF0aW9uLiBTZWVcbiAgICAgKiB7QGxpbmsgaHR0cHM6Ly9jbG91ZC5nb29nbGUuY29tL2Z1bmN0aW9ucy9kb2NzL3JlZmVyZW5jZS9yZXN0L3YxL3Byb2plY3RzLmxvY2F0aW9ucy5mdW5jdGlvbnMjQ2xvdWRGdW5jdGlvbiB8IHByb2plY3RzLmxvY2F0aW9ucy5mdW5jdGlvbnN9LlxuICAgICAqIEByZW1hcmtzXG4gICAgICogSWYgeW91IG5lZWQgc3BlY2lhbGl6ZWQgb3B0aW9ucywgeW91IGNhbiBwYXNzIHRoZW0gdG8gdGhlIEdvb2dsZSBDbG91ZFxuICAgICAqIEZ1bmN0aW9ucyBBUEkgZGlyZWN0bHkuIE5vdGUgdGhhdCBpZiB5b3Ugb3ZlcnJpZGUgYW55IHNldHRpbmdzIHNldCBieVxuICAgICAqIGZhYXN0LmpzLCB5b3UgbWF5IGNhdXNlIGZhYXN0LmpzIHRvIG5vdCB3b3JrOlxuICAgICAqXG4gICAgICogYGBgdHlwZXNjcmlwdFxuICAgICAqICBjb25zdCByZXF1ZXN0Qm9keTogQ2xvdWRGdW5jdGlvbnMuU2NoZW1hJENsb3VkRnVuY3Rpb24gPSB7XG4gICAgICogICAgICBuYW1lLFxuICAgICAqICAgICAgZW50cnlQb2ludDogXCJ0cmFtcG9saW5lXCIsXG4gICAgICogICAgICB0aW1lb3V0LFxuICAgICAqICAgICAgYXZhaWxhYmxlTWVtb3J5TWIsXG4gICAgICogICAgICBzb3VyY2VVcGxvYWRVcmwsXG4gICAgICogICAgICBydW50aW1lOiBcIm5vZGVqczE0XCIsXG4gICAgICogICAgICAuLi5nb29nbGVDbG91ZEZ1bmN0aW9uT3B0aW9uc1xuICAgICAqICB9O1xuICAgICAqIGBgYFxuICAgICAqXG4gICAgICovXG4gICAgZ29vZ2xlQ2xvdWRGdW5jdGlvbk9wdGlvbnM/OiBDbG91ZEZ1bmN0aW9ucy5TY2hlbWEkQ2xvdWRGdW5jdGlvbjtcblxuICAgIC8qKiBAaW50ZXJuYWwgKi9cbiAgICBfZ2NXb3JrZXI/OiAocmVzb3VyY2VzOiBHb29nbGVSZXNvdXJjZXMsIHNlcnZpY2VzOiBHb29nbGVTZXJ2aWNlcykgPT4gUHJvbWlzZTx2b2lkPjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBHb29nbGVSZXNvdXJjZXMge1xuICAgIHRyYW1wb2xpbmU6IHN0cmluZztcbiAgICByZXF1ZXN0UXVldWVUb3BpYz86IHN0cmluZztcbiAgICByZXF1ZXN0U3Vic2NyaXB0aW9uPzogc3RyaW5nO1xuICAgIHJlc3BvbnNlUXVldWVUb3BpYz86IHN0cmluZztcbiAgICByZXNwb25zZVN1YnNjcmlwdGlvbj86IHN0cmluZztcbiAgICByZWdpb246IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBHb29nbGVDbG91ZFByaWNpbmcge1xuICAgIHBlckludm9jYXRpb246IG51bWJlcjtcbiAgICBwZXJHaHpTZWNvbmQ6IG51bWJlcjtcbiAgICBwZXJHYlNlY29uZDogbnVtYmVyO1xuICAgIHBlckdiT3V0Ym91bmREYXRhOiBudW1iZXI7XG4gICAgcGVyR2JQdWJTdWI6IG51bWJlcjtcbn1cblxuZXhwb3J0IGNsYXNzIEdvb2dsZU1ldHJpY3Mge1xuICAgIG91dGJvdW5kQnl0ZXMgPSAwO1xuICAgIHB1YlN1YkJ5dGVzID0gMDtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBHb29nbGVTZXJ2aWNlcyB7XG4gICAgcmVhZG9ubHkgY2xvdWRGdW5jdGlvbnM6IENsb3VkRnVuY3Rpb25zLkNsb3VkZnVuY3Rpb25zO1xuICAgIHJlYWRvbmx5IHB1YnN1YjogUHViU3ViQXBpLlB1YnN1YjtcbiAgICByZWFkb25seSBnb29nbGU6IEdvb2dsZUFwaXM7XG4gICAgcmVhZG9ubHkgY2xvdWRCaWxsaW5nOiBDbG91ZEJpbGxpbmcuQ2xvdWRiaWxsaW5nO1xufVxuXG4vKipcbiAqIEBpbnRlcm5hbFxuICovXG5leHBvcnQgaW50ZXJmYWNlIEdvb2dsZVN0YXRlIHtcbiAgICByZXNvdXJjZXM6IEdvb2dsZVJlc291cmNlcztcbiAgICBzZXJ2aWNlczogR29vZ2xlU2VydmljZXM7XG4gICAgdXJsPzogc3RyaW5nO1xuICAgIHByb2plY3Q6IHN0cmluZztcbiAgICBmdW5jdGlvbk5hbWU6IHN0cmluZztcbiAgICBtZXRyaWNzOiBHb29nbGVNZXRyaWNzO1xuICAgIG9wdGlvbnM6IFJlcXVpcmVkPEdvb2dsZU9wdGlvbnM+O1xuICAgIGdjUHJvbWlzZT86IFByb21pc2U8dm9pZD47XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBkZWZhdWx0R2NXb3JrZXIocmVzb3VyY2VzOiBHb29nbGVSZXNvdXJjZXMsIHNlcnZpY2VzOiBHb29nbGVTZXJ2aWNlcykge1xuICAgIHJldHVybiBkZWxldGVSZXNvdXJjZXMoc2VydmljZXMsIHJlc291cmNlcywgbG9nLmdjKTtcbn1cblxuZXhwb3J0IGNvbnN0IGRlZmF1bHRzOiBSZXF1aXJlZDxHb29nbGVPcHRpb25zPiA9IHtcbiAgICAuLi5jb21tb25EZWZhdWx0cyxcbiAgICByZWdpb246IFwidXMtY2VudHJhbDFcIixcbiAgICBnb29nbGVDbG91ZEZ1bmN0aW9uT3B0aW9uczoge30sXG4gICAgX2djV29ya2VyOiBkZWZhdWx0R2NXb3JrZXJcbn07XG5cbmV4cG9ydCBjb25zdCBHb29nbGVJbXBsOiBQcm92aWRlckltcGw8R29vZ2xlT3B0aW9ucywgR29vZ2xlU3RhdGU+ID0ge1xuICAgIG5hbWU6IFwiZ29vZ2xlXCIsXG4gICAgaW5pdGlhbGl6ZSxcbiAgICBkZWZhdWx0cyxcbiAgICBjbGVhbnVwLFxuICAgIGNvc3RTbmFwc2hvdCxcbiAgICBsb2dVcmwsXG4gICAgaW52b2tlLFxuICAgIHBvbGwsXG4gICAgcmVzcG9uc2VRdWV1ZUlkXG59O1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5pdGlhbGl6ZUdvb2dsZVNlcnZpY2VzKCk6IFByb21pc2U8R29vZ2xlU2VydmljZXM+IHtcbiAgICBjb25zdCBhdXRoID0gYXdhaXQgZ29vZ2xlLmF1dGguZ2V0Q2xpZW50KHtcbiAgICAgICAgc2NvcGVzOiBbXCJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9hdXRoL2Nsb3VkLXBsYXRmb3JtXCJdXG4gICAgfSk7XG5cbiAgICBnb29nbGUub3B0aW9ucyh7XG4gICAgICAgIGF1dGgsXG4gICAgICAgIHJldHJ5Q29uZmlnOiB7XG4gICAgICAgICAgICByZXRyeTogOCxcbiAgICAgICAgICAgIHJldHJ5RGVsYXk6IDI1MCxcbiAgICAgICAgICAgIG5vUmVzcG9uc2VSZXRyaWVzOiAzLFxuICAgICAgICAgICAgc2hvdWxkUmV0cnk6IHNob3VsZFJldHJ5UmVxdWVzdChsb2cuaW5mbylcbiAgICAgICAgfVxuICAgIH0pO1xuICAgIHJldHVybiB7XG4gICAgICAgIGNsb3VkRnVuY3Rpb25zOiBnb29nbGUuY2xvdWRmdW5jdGlvbnMoXCJ2MVwiKSxcbiAgICAgICAgcHVic3ViOiBnb29nbGUucHVic3ViKFwidjFcIiksXG4gICAgICAgIGNsb3VkQmlsbGluZzogZ29vZ2xlLmNsb3VkYmlsbGluZyhcInYxXCIpLFxuICAgICAgICBnb29nbGVcbiAgICB9O1xufVxuXG5pbnRlcmZhY2UgUG9sbE9wdGlvbnMge1xuICAgIG1heFJldHJpZXM/OiBudW1iZXI7XG4gICAgZGVsYXk/OiAocmV0cmllczogbnVtYmVyKSA9PiBQcm9taXNlPHZvaWQ+O1xufVxuXG5pbnRlcmZhY2UgUG9sbENvbmZpZzxUPiBleHRlbmRzIFBvbGxPcHRpb25zIHtcbiAgICByZXF1ZXN0OiAoKSA9PiBQcm9taXNlPFQ+O1xuICAgIGNoZWNrRG9uZTogKHJlc3VsdDogVCkgPT4gYm9vbGVhbjtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVmYXVsdFBvbGxEZWxheShyZXRyaWVzOiBudW1iZXIpIHtcbiAgICBpZiAocmV0cmllcyA+IDUpIHtcbiAgICAgICAgYXdhaXQgc2xlZXAoNSAqIDEwMDApO1xuICAgIH1cbiAgICBhd2FpdCBzbGVlcCgocmV0cmllcyArIDEpICogNTAwKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gcG9sbE9wZXJhdGlvbjxUPih7XG4gICAgcmVxdWVzdCxcbiAgICBjaGVja0RvbmUsXG4gICAgZGVsYXkgPSBkZWZhdWx0UG9sbERlbGF5LFxuICAgIG1heFJldHJpZXMgPSA1MFxufTogUG9sbENvbmZpZzxUPik6IFByb21pc2U8VCB8IHVuZGVmaW5lZD4ge1xuICAgIGxldCByZXRyaWVzID0gMDtcbiAgICBhd2FpdCBkZWxheShyZXRyaWVzKTtcbiAgICB3aGlsZSAodHJ1ZSkge1xuICAgICAgICBsb2cuaW5mbyhgUG9sbGluZy4uLmApO1xuICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCByZXF1ZXN0KCk7XG4gICAgICAgIGlmIChjaGVja0RvbmUocmVzdWx0KSkge1xuICAgICAgICAgICAgbG9nLmluZm8oYERvbmUuYCk7XG4gICAgICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgICB9XG4gICAgICAgIGlmIChyZXRyaWVzKysgPj0gbWF4UmV0cmllcykge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEZhYXN0RXJyb3IoYFRpbWVkIG91dCBhZnRlciAke3JldHJpZXN9IGF0dGVtcHRzLmApO1xuICAgICAgICB9XG4gICAgICAgIGF3YWl0IGRlbGF5KHJldHJpZXMpO1xuICAgIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gcXVpZXRseTxUPihwcm9taXNlOiBHYXhpb3NQcm9taXNlPFQ+KSB7XG4gICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcHJvbWlzZTtcbiAgICAgICAgcmV0dXJuIHJlc3VsdC5kYXRhO1xuICAgIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG59XG5cbmNvbnN0IHRocm90dGxlR29vZ2xlV3JpdGUgPSB0aHJvdHRsZShcbiAgICB7XG4gICAgICAgIGNvbmN1cnJlbmN5OiA0LFxuICAgICAgICByYXRlOiAzLFxuICAgICAgICByZXRyeTogKGVyciwgbikgPT4ge1xuICAgICAgICAgICAgY29uc3QgeyBtZXNzYWdlIH0gPSBlcnIgYXMgRXJyb3I7XG4gICAgICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgICAgIG4gPCA2ICYmXG4gICAgICAgICAgICAgICAgKG1lc3NhZ2UubWF0Y2goL0J1aWxkIGZhaWxlZC8pICE9PSBudWxsIHx8XG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UubWF0Y2goL1F1b3RhLykgIT09IG51bGwgfHxcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZS5tYXRjaCgvbG9hZCBhdHRlbXB0IHRpbWVkIG91dC8pICE9PSBudWxsIHx8XG4gICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UubWF0Y2goL0VDT05OUkVTRVQvKSAhPT0gbnVsbCB8fFxuICAgICAgICAgICAgICAgICAgICBtZXNzYWdlLm1hdGNoKC9mYWlsZWQgb24gbG9hZGluZyB1c2VyIGNvZGUvKSAhPSBudWxsKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgIH0sXG4gICAgPFQ+KG9wOiAoKSA9PiBQcm9taXNlPFQ+KSA9PiBvcCgpXG4pO1xuXG5hc3luYyBmdW5jdGlvbiB3YWl0Rm9yKFxuICAgIGFwaTogQ2xvdWRGdW5jdGlvbnMuQ2xvdWRmdW5jdGlvbnMsXG4gICAgcmVzcG9uc2U6ICgpID0+IEdheGlvc1Byb21pc2U8Q2xvdWRGdW5jdGlvbnMuU2NoZW1hJE9wZXJhdGlvbj5cbikge1xuICAgIHJldHVybiB0aHJvdHRsZUdvb2dsZVdyaXRlKGFzeW5jICgpID0+IHtcbiAgICAgICAgbGV0IG9wZXJhdGlvbjogR2F4aW9zUmVzcG9uc2U8Q2xvdWRGdW5jdGlvbnMuU2NoZW1hJE9wZXJhdGlvbj47XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBvcGVyYXRpb24gPSBhd2FpdCByZXNwb25zZSgpO1xuICAgICAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEZhYXN0RXJyb3IoZXJyLCBcImNvdWxkIG5vdCBnZXQgb3BlcmF0aW9uXCIpO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IG9wZXJhdGlvbk5hbWUgPSBvcGVyYXRpb24uZGF0YS5uYW1lITtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIHJldHVybiBwb2xsT3BlcmF0aW9uKHtcbiAgICAgICAgICAgICAgICByZXF1ZXN0OiAoKSA9PiBxdWlldGx5KGFwaS5vcGVyYXRpb25zLmdldCh7IG5hbWU6IG9wZXJhdGlvbk5hbWUgfSkpLFxuICAgICAgICAgICAgICAgIGNoZWNrRG9uZTogcmVzdWx0ID0+IHtcbiAgICAgICAgICAgICAgICAgICAgLyogaXN0YW5idWwgaWdub3JlIGlmICAqL1xuICAgICAgICAgICAgICAgICAgICBpZiAoIXJlc3VsdCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBpZiAqL1xuICAgICAgICAgICAgICAgICAgICBpZiAocmVzdWx0LmVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCB1bmRlcmx5aW5nID0gbmV3IEZhYXN0RXJyb3IoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0LmVycm9yLm1lc3NhZ2UgPz8gdW5kZWZpbmVkXG4gICAgICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICAgICAgdW5kZXJseWluZy5zdGFjayA9IFwiXCI7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRmFhc3RFcnJvcih1bmRlcmx5aW5nLCBcIkVycm9yIHBvbGxpbmcgb3BlcmF0aW9uXCIpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiByZXN1bHQuZG9uZSB8fCBmYWxzZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSBjYXRjaCAoZXJyOiBhbnkpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBGYWFzdEVycm9yKGVyciwgXCJwb2xsIG9wZXJhdGlvbiBmYWlsZWRcIik7XG4gICAgICAgIH1cbiAgICB9KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZGVsZXRlRnVuY3Rpb24oYXBpOiBDbG91ZEZ1bmN0aW9ucy5DbG91ZGZ1bmN0aW9ucywgcGF0aDogc3RyaW5nKSB7XG4gICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIGF3YWl0IHdhaXRGb3IoYXBpLCAoKSA9PlxuICAgICAgICAgICAgYXBpLnByb2plY3RzLmxvY2F0aW9ucy5mdW5jdGlvbnMuZGVsZXRlKHtcbiAgICAgICAgICAgICAgICBuYW1lOiBwYXRoXG4gICAgICAgICAgICB9KVxuICAgICAgICApO1xuICAgIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgICAgIGlmIChlcnIubWVzc2FnZS5tYXRjaCgvZG9lcyBub3QgZXhpc3QvKSkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHRocm93IGVycjtcbiAgICB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBpbml0aWFsaXplKFxuICAgIGZtb2R1bGU6IHN0cmluZyxcbiAgICBub25jZTogVVVJRCxcbiAgICBvcHRpb25zOiBSZXF1aXJlZDxHb29nbGVPcHRpb25zPlxuKTogUHJvbWlzZTxHb29nbGVTdGF0ZT4ge1xuICAgIGxvZy5pbmZvKGBDcmVhdGUgZ29vZ2xlIGNsb3VkIGZ1bmN0aW9uYCk7XG4gICAgY29uc3Qgc2VydmljZXMgPSBhd2FpdCBpbml0aWFsaXplR29vZ2xlU2VydmljZXMoKTtcbiAgICBjb25zdCBwcm9qZWN0ID0gYXdhaXQgZ29vZ2xlLmF1dGguZ2V0UHJvamVjdElkKCk7XG4gICAgY29uc3QgeyBjbG91ZEZ1bmN0aW9ucywgcHVic3ViIH0gPSBzZXJ2aWNlcztcbiAgICBjb25zdCB7IHJlZ2lvbiB9ID0gb3B0aW9ucztcbiAgICBjb25zdCBsb2NhdGlvbiA9IGBwcm9qZWN0cy8ke3Byb2plY3R9L2xvY2F0aW9ucy8ke3JlZ2lvbn1gO1xuICAgIGNvbnN0IGZ1bmN0aW9uTmFtZSA9IFwiZmFhc3QtXCIgKyBub25jZTtcblxuICAgIGNvbnN0IHsgdGltZW91dCB9ID0gb3B0aW9ucztcbiAgICBjb25zdCB7IHdyYXBwZXJWZXJib3NlIH0gPSBvcHRpb25zLmRlYnVnT3B0aW9ucztcbiAgICBhc3luYyBmdW5jdGlvbiBjcmVhdGVDb2RlQnVuZGxlKCkge1xuICAgICAgICBjb25zdCB3cmFwcGVyT3B0aW9ucyA9IHtcbiAgICAgICAgICAgIGNoaWxkUHJvY2Vzc1RpbWVvdXRNczogTWF0aC5tYXgoMTAwMCwgKHRpbWVvdXQgLSA1KSAqIDEwMDApLFxuICAgICAgICAgICAgd3JhcHBlclZlcmJvc2VcbiAgICAgICAgfTtcbiAgICAgICAgY29uc3QgeyBhcmNoaXZlIH0gPSBhd2FpdCBnb29nbGVQYWNrZXIoXG4gICAgICAgICAgICBmbW9kdWxlLFxuICAgICAgICAgICAgb3B0aW9ucyxcbiAgICAgICAgICAgIHdyYXBwZXJPcHRpb25zLFxuICAgICAgICAgICAgZnVuY3Rpb25OYW1lXG4gICAgICAgICk7XG4gICAgICAgIGNvbnN0IHVwbG9hZFVybFJlc3BvbnNlID0gYXdhaXQgdGhyb3R0bGVHb29nbGVXcml0ZSgoKSA9PlxuICAgICAgICAgICAgY2xvdWRGdW5jdGlvbnMucHJvamVjdHMubG9jYXRpb25zLmZ1bmN0aW9ucy5nZW5lcmF0ZVVwbG9hZFVybCh7XG4gICAgICAgICAgICAgICAgcGFyZW50OiBsb2NhdGlvblxuICAgICAgICAgICAgfSlcbiAgICAgICAgKTtcblxuICAgICAgICBjb25zdCB1cGxvYWRSZXN1bHQgPSBhd2FpdCB1cGxvYWRaaXAodXBsb2FkVXJsUmVzcG9uc2UuZGF0YS51cGxvYWRVcmwhLCBhcmNoaXZlKTtcbiAgICAgICAgbG9nLmluZm8oYFVwbG9hZCB6aXAgZmlsZSByZXNwb25zZTogJHt1cGxvYWRSZXN1bHQ/LnN0YXR1c1RleHR9YCk7XG4gICAgICAgIHJldHVybiB1cGxvYWRVcmxSZXNwb25zZS5kYXRhLnVwbG9hZFVybDtcbiAgICB9XG5cbiAgICBjb25zdCB0cmFtcG9saW5lID0gYHByb2plY3RzLyR7cHJvamVjdH0vbG9jYXRpb25zLyR7cmVnaW9ufS9mdW5jdGlvbnMvJHtmdW5jdGlvbk5hbWV9YDtcblxuICAgIGNvbnN0IHJlc291cmNlczogR29vZ2xlUmVzb3VyY2VzID0ge1xuICAgICAgICB0cmFtcG9saW5lLFxuICAgICAgICByZWdpb25cbiAgICB9O1xuICAgIGNvbnN0IHN0YXRlOiBHb29nbGVTdGF0ZSA9IHtcbiAgICAgICAgcmVzb3VyY2VzLFxuICAgICAgICBzZXJ2aWNlcyxcbiAgICAgICAgcHJvamVjdCxcbiAgICAgICAgZnVuY3Rpb25OYW1lLFxuICAgICAgICBtZXRyaWNzOiBuZXcgR29vZ2xlTWV0cmljcygpLFxuICAgICAgICBvcHRpb25zXG4gICAgfTtcblxuICAgIGNvbnN0IHsgZ2MsIHJldGVudGlvbkluRGF5cywgX2djV29ya2VyOiBnY1dvcmtlciB9ID0gb3B0aW9ucztcbiAgICBpZiAoZ2MgPT09IFwiYXV0b1wiIHx8IGdjID09PSBcImZvcmNlXCIpIHtcbiAgICAgICAgbG9nLmdjKGBTdGFydGluZyBnYXJiYWdlIGNvbGxlY3RvcmApO1xuICAgICAgICBzdGF0ZS5nY1Byb21pc2UgPSBjb2xsZWN0R2FyYmFnZShcbiAgICAgICAgICAgIGdjV29ya2VyLFxuICAgICAgICAgICAgc2VydmljZXMsXG4gICAgICAgICAgICBwcm9qZWN0LFxuICAgICAgICAgICAgcmV0ZW50aW9uSW5EYXlzXG4gICAgICAgICkuY2F0Y2goZXJyID0+IHtcbiAgICAgICAgICAgIGxvZy5nYyhgR2FyYmFnZSBjb2xsZWN0aW9uIGVycm9yOiAke2Vycn1gKTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgY29uc3QgcHJpY2luZ1Byb21pc2UgPSBnZXRHb29nbGVDbG91ZEZ1bmN0aW9uc1ByaWNpbmcoc2VydmljZXMuY2xvdWRCaWxsaW5nLCByZWdpb24pO1xuXG4gICAgY29uc3QgeyBtb2RlIH0gPSBvcHRpb25zO1xuXG4gICAgY29uc3QgcmVzcG9uc2VRdWV1ZVByb21pc2UgPSAoYXN5bmMgKCkgPT4ge1xuICAgICAgICBjb25zdCB0b3BpYyA9IGF3YWl0IHB1YnN1Yi5wcm9qZWN0cy50b3BpY3MuY3JlYXRlKHtcbiAgICAgICAgICAgIG5hbWU6IGdldFJlc3BvbnNlUXVldWVUb3BpYyhwcm9qZWN0LCBmdW5jdGlvbk5hbWUpXG4gICAgICAgIH0pO1xuXG4gICAgICAgIHJlc291cmNlcy5yZXNwb25zZVF1ZXVlVG9waWMgPSB0b3BpYy5kYXRhLm5hbWUgPz8gdW5kZWZpbmVkO1xuICAgICAgICByZXNvdXJjZXMucmVzcG9uc2VTdWJzY3JpcHRpb24gPSBnZXRSZXNwb25zZVN1YnNjcmlwdGlvbihwcm9qZWN0LCBmdW5jdGlvbk5hbWUpO1xuICAgICAgICBsb2cuaW5mbyhgQ3JlYXRpbmcgcmVzcG9uc2UgcXVldWUgc3Vic2NyaXB0aW9uYCk7XG4gICAgICAgIGF3YWl0IHB1YnN1Yi5wcm9qZWN0cy5zdWJzY3JpcHRpb25zLmNyZWF0ZSh7XG4gICAgICAgICAgICBuYW1lOiByZXNvdXJjZXMucmVzcG9uc2VTdWJzY3JpcHRpb24sXG4gICAgICAgICAgICByZXF1ZXN0Qm9keToge1xuICAgICAgICAgICAgICAgIHRvcGljOiByZXNvdXJjZXMucmVzcG9uc2VRdWV1ZVRvcGljXG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH0pKCk7XG5cbiAgICBsZXQgcmVxdWVzdFF1ZXVlUHJvbWlzZTtcbiAgICBpZiAobW9kZSA9PT0gXCJxdWV1ZVwiKSB7XG4gICAgICAgIGxvZy5pbmZvKGBJbml0aWFsaXppbmcgcXVldWVgKTtcbiAgICAgICAgcmVzb3VyY2VzLnJlcXVlc3RRdWV1ZVRvcGljID0gZ2V0UmVxdWVzdFF1ZXVlVG9waWMocHJvamVjdCwgZnVuY3Rpb25OYW1lKTtcbiAgICAgICAgcmVxdWVzdFF1ZXVlUHJvbWlzZSA9IHB1YnN1Yi5wcm9qZWN0cy50b3BpY3MuY3JlYXRlKHtcbiAgICAgICAgICAgIG5hbWU6IHJlc291cmNlcy5yZXF1ZXN0UXVldWVUb3BpY1xuICAgICAgICB9KTtcbiAgICAgICAgcmVzb3VyY2VzLnJlcXVlc3RTdWJzY3JpcHRpb24gPSBnZXRSZXF1ZXN0U3Vic2NyaXB0aW9uKFxuICAgICAgICAgICAgcHJvamVjdCxcbiAgICAgICAgICAgIGZ1bmN0aW9uTmFtZSxcbiAgICAgICAgICAgIHJlZ2lvblxuICAgICAgICApO1xuICAgIH1cblxuICAgIGNvbnN0IHNvdXJjZVVwbG9hZFVybCA9IGF3YWl0IGNyZWF0ZUNvZGVCdW5kbGUoKTtcbiAgICBjb25zdCB7IG1lbW9yeVNpemUsIGdvb2dsZUNsb3VkRnVuY3Rpb25PcHRpb25zLCBlbnYgfSA9IG9wdGlvbnM7XG4gICAgaWYgKCFHb29nbGVDbG91ZEZ1bmN0aW9uc01lbW9yeVNpemVzLmZpbmQoc2l6ZSA9PiBzaXplID09PSBtZW1vcnlTaXplKSkge1xuICAgICAgICBsb2cud2FybihgSW52YWxpZCBtZW1vcnlTaXplICR7bWVtb3J5U2l6ZX0gZm9yIEdvb2dsZSBDbG91ZCBGdW5jdGlvbnNgKTtcbiAgICB9XG4gICAgY29uc3QgcmVxdWVzdEJvZHk6IENsb3VkRnVuY3Rpb25zLlNjaGVtYSRDbG91ZEZ1bmN0aW9uID0ge1xuICAgICAgICBuYW1lOiB0cmFtcG9saW5lLFxuICAgICAgICBlbnRyeVBvaW50OiBcInRyYW1wb2xpbmVcIixcbiAgICAgICAgdGltZW91dDogYCR7dGltZW91dH1zYCxcbiAgICAgICAgYXZhaWxhYmxlTWVtb3J5TWI6IG1lbW9yeVNpemUsXG4gICAgICAgIHNvdXJjZVVwbG9hZFVybCxcbiAgICAgICAgZW52aXJvbm1lbnRWYXJpYWJsZXM6IGVudixcbiAgICAgICAgcnVudGltZTogXCJub2RlanMxNFwiLFxuICAgICAgICAuLi5nb29nbGVDbG91ZEZ1bmN0aW9uT3B0aW9uc1xuICAgIH07XG4gICAgaWYgKG1vZGUgPT09IFwicXVldWVcIikge1xuICAgICAgICBhd2FpdCByZXF1ZXN0UXVldWVQcm9taXNlO1xuICAgICAgICByZXF1ZXN0Qm9keS5ldmVudFRyaWdnZXIgPSB7XG4gICAgICAgICAgICBldmVudFR5cGU6IFwicHJvdmlkZXJzL2Nsb3VkLnB1YnN1Yi9ldmVudFR5cGVzL3RvcGljLnB1Ymxpc2hcIixcbiAgICAgICAgICAgIHJlc291cmNlOiByZXNvdXJjZXMucmVxdWVzdFF1ZXVlVG9waWNcbiAgICAgICAgfTtcbiAgICB9IGVsc2Uge1xuICAgICAgICByZXF1ZXN0Qm9keS5odHRwc1RyaWdnZXIgPSB7fTtcbiAgICB9XG4gICAgbG9nLmluZm8oYENyZWF0ZSBmdW5jdGlvbiBhdCAke2xvY2F0aW9ufWApO1xuICAgIGxvZy5pbmZvKGBSZXF1ZXN0IGJvZHk6ICVPYCwgcmVxdWVzdEJvZHkpO1xuICAgIGF3YWl0IHJldHJ5T3AoMywgYXN5bmMgKCkgPT4ge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgbG9nLmluZm8oYGNyZWF0ZSBmdW5jdGlvbiAke3JlcXVlc3RCb2R5Lm5hbWV9IFske29wdGlvbnMuZGVzY3JpcHRpb259XWApO1xuICAgICAgICAgICAgYXdhaXQgd2FpdEZvcihjbG91ZEZ1bmN0aW9ucywgKCkgPT5cbiAgICAgICAgICAgICAgICBjbG91ZEZ1bmN0aW9ucy5wcm9qZWN0cy5sb2NhdGlvbnMuZnVuY3Rpb25zLmNyZWF0ZSh7XG4gICAgICAgICAgICAgICAgICAgIGxvY2F0aW9uLFxuICAgICAgICAgICAgICAgICAgICByZXF1ZXN0Qm9keVxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgYXdhaXQgY2xvdWRGdW5jdGlvbnMucHJvamVjdHMubG9jYXRpb25zLmZ1bmN0aW9ucy5zZXRJYW1Qb2xpY3koe1xuICAgICAgICAgICAgICAgIHJlc291cmNlOiB0cmFtcG9saW5lLFxuICAgICAgICAgICAgICAgIHJlcXVlc3RCb2R5OiB7XG4gICAgICAgICAgICAgICAgICAgIHBvbGljeToge1xuICAgICAgICAgICAgICAgICAgICAgICAgYmluZGluZ3M6IFtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lbWJlcnM6IFtcImFsbFVzZXJzXCJdLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb2xlOiBcInJvbGVzL2Nsb3VkZnVuY3Rpb25zLmludm9rZXJcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIF1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICAgICAgLyogaXN0YW5idWwgaWdub3JlIG5leHQgICovXG4gICAgICAgICAgICBhd2FpdCBkZWxldGVGdW5jdGlvbihjbG91ZEZ1bmN0aW9ucywgdHJhbXBvbGluZSkuY2F0Y2goKCkgPT4ge30pO1xuICAgICAgICAgICAgdGhyb3cgbmV3IEZhYXN0RXJyb3IoXG4gICAgICAgICAgICAgICAgeyBjYXVzZTogZXJyLCBuYW1lOiBGYWFzdEVycm9yTmFtZXMuRUNSRUFURSB9LFxuICAgICAgICAgICAgICAgIFwiZmFpbGVkIHRvIGNyZWF0ZSBnb29nbGUgY2xvdWQgZnVuY3Rpb25cIlxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgIH0pO1xuICAgIGlmIChtb2RlID09PSBcImh0dHBzXCIgfHwgbW9kZSA9PT0gXCJhdXRvXCIpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IGZ1bmMgPSBhd2FpdCBjbG91ZEZ1bmN0aW9ucy5wcm9qZWN0cy5sb2NhdGlvbnMuZnVuY3Rpb25zLmdldCh7XG4gICAgICAgICAgICAgICAgbmFtZTogdHJhbXBvbGluZVxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIGlmICghZnVuYy5kYXRhLmh0dHBzVHJpZ2dlcikge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBGYWFzdEVycm9yKFwiQ291bGQgbm90IGdldCBodHRwIHRyaWdnZXIgdXJsXCIpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3QgeyB1cmwgfSA9IGZ1bmMuZGF0YS5odHRwc1RyaWdnZXIhO1xuICAgICAgICAgICAgaWYgKCF1cmwpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRmFhc3RFcnJvcihcIkNvdWxkIG5vdCBnZXQgaHR0cCB0cmlnZ2VyIHVybFwiKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGxvZy5pbmZvKGBGdW5jdGlvbiBVUkw6ICR7dXJsfWApO1xuICAgICAgICAgICAgc3RhdGUudXJsID0gdXJsO1xuICAgICAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEZhYXN0RXJyb3IoXG4gICAgICAgICAgICAgICAgZXJyLFxuICAgICAgICAgICAgICAgIGBDb3VsZCBub3QgZ2V0IGZ1bmN0aW9uICR7dHJhbXBvbGluZX0gb3IgaXRzIHVybCwgZGVzcGl0ZSBpdCBiZWluZyBjcmVhdGVkYFxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICBhd2FpdCBwcmljaW5nUHJvbWlzZTtcbiAgICBhd2FpdCByZXNwb25zZVF1ZXVlUHJvbWlzZTtcbiAgICByZXR1cm4gc3RhdGU7XG59XG5cbmZ1bmN0aW9uIGdldFJlcXVlc3RRdWV1ZVRvcGljKHByb2plY3Q6IHN0cmluZywgZnVuY3Rpb25OYW1lOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gYHByb2plY3RzLyR7cHJvamVjdH0vdG9waWNzLyR7ZnVuY3Rpb25OYW1lfS1SZXF1ZXN0c2A7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRSZXNwb25zZVF1ZXVlVG9waWMocHJvamVjdDogc3RyaW5nLCBmdW5jdGlvbk5hbWU6IHN0cmluZykge1xuICAgIHJldHVybiBgcHJvamVjdHMvJHtwcm9qZWN0fS90b3BpY3MvJHtmdW5jdGlvbk5hbWV9LVJlc3BvbnNlc2A7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRSZXNwb25zZVN1YnNjcmlwdGlvbihwcm9qZWN0OiBzdHJpbmcsIGZ1bmN0aW9uTmFtZTogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGBwcm9qZWN0cy8ke3Byb2plY3R9L3N1YnNjcmlwdGlvbnMvJHtmdW5jdGlvbk5hbWV9LVJlc3BvbnNlc2A7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRSZXF1ZXN0U3Vic2NyaXB0aW9uKFxuICAgIHByb2plY3Q6IHN0cmluZyxcbiAgICBmdW5jdGlvbk5hbWU6IHN0cmluZyxcbiAgICByZWdpb246IHN0cmluZ1xuKSB7XG4gICAgcmV0dXJuIGBwcm9qZWN0cy8ke3Byb2plY3R9L3N1YnNjcmlwdGlvbnMvZ2NmLSR7ZnVuY3Rpb25OYW1lfS0ke3JlZ2lvbn0tJHtmdW5jdGlvbk5hbWV9LVJlcXVlc3RzYDtcbn1cblxuY29uc3QgYWdlbnQgPSBuZXcgaHR0cHMuQWdlbnQoeyBrZWVwQWxpdmU6IHRydWUsIHRpbWVvdXQ6IDAsIG1heFNvY2tldHM6IDEwMDAgfSk7XG5cbmFzeW5jIGZ1bmN0aW9uIGNhbGxGdW5jdGlvbkh0dHBzKFxuICAgIHVybDogc3RyaW5nLFxuICAgIGNhbGw6IEZ1bmN0aW9uQ2FsbCxcbiAgICBtZXRyaWNzOiBHb29nbGVNZXRyaWNzLFxuICAgIGNhbmNlbDogUHJvbWlzZTx2b2lkPlxuKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3Qgc291cmNlID0gbmV3IEFib3J0Q29udHJvbGxlcigpO1xuICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IGF4aW9zQ29uZmlnOiBHYXhpb3NPcHRpb25zID0ge1xuICAgICAgICAgICAgbWV0aG9kOiBcIlBPU1RcIixcbiAgICAgICAgICAgIHVybCxcbiAgICAgICAgICAgIGhlYWRlcnM6IHsgXCJDb250ZW50LVR5cGVcIjogXCJhcHBsaWNhdGlvbi9qc29uXCIgfSxcbiAgICAgICAgICAgIGJvZHk6IHNlcmlhbGl6ZShjYWxsKSxcbiAgICAgICAgICAgIHNpZ25hbDogc291cmNlLnNpZ25hbCxcbiAgICAgICAgICAgIHJlc3BvbnNlVHlwZTogXCJqc29uXCIsXG4gICAgICAgICAgICByZXRyeTogZmFsc2UsXG4gICAgICAgICAgICBhZ2VudFxuICAgICAgICB9O1xuICAgICAgICBjb25zdCByYXdSZXNwb25zZSA9IGF3YWl0IFByb21pc2UucmFjZShbXG4gICAgICAgICAgICBnYXhpb3MucmVxdWVzdDx2b2lkPihheGlvc0NvbmZpZyksXG4gICAgICAgICAgICBjYW5jZWxcbiAgICAgICAgXSk7XG5cbiAgICAgICAgaWYgKCFyYXdSZXNwb25zZSkge1xuICAgICAgICAgICAgbG9nLmluZm8oYGNhbmNlbGxpbmcgZ2NwIGludm9rZWApO1xuICAgICAgICAgICAgc291cmNlLmFib3J0KCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIG1ldHJpY3Mub3V0Ym91bmRCeXRlcyArPSBjb21wdXRlSHR0cFJlc3BvbnNlQnl0ZXMocmF3UmVzcG9uc2UhLmhlYWRlcnMpO1xuICAgICAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEZhYXN0RXJyb3IoXG4gICAgICAgICAgICAgICAgZXJyLFxuICAgICAgICAgICAgICAgIGBDb3VsZCBub3QgcGFyc2UgJHt1dGlsLmluc3BlY3QocmF3UmVzcG9uc2UuZGF0YSl9YFxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgICAgIGNvbnN0IHsgcmVzcG9uc2UgfSA9IGVycjtcbiAgICAgICAgaWYgKHJlc3BvbnNlKSB7XG4gICAgICAgICAgICBpZiAocmVzcG9uc2Uuc3RhdHVzID09PSA1MDMpIHtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRmFhc3RFcnJvcihcbiAgICAgICAgICAgICAgICAgICAgeyBjYXVzZTogZXJyLCBuYW1lOiBGYWFzdEVycm9yTmFtZXMuRU1FTU9SWSB9LFxuICAgICAgICAgICAgICAgICAgICBcImdvb2dsZSBjbG91ZCBmdW5jdGlvbjogcG9zc2libHkgb3V0IG9mIG1lbW9yeVwiXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdGhyb3cgbmV3IEZhYXN0RXJyb3IoXG4gICAgICAgICAgICAgICAgZXJyLFxuICAgICAgICAgICAgICAgIGB3aGVuIGludm9raW5nIGdvb2dsZSBjbG91ZCBmdW5jdGlvbjogJXNcXG5EZXRhaWxzOiAlc2AsXG4gICAgICAgICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzVGV4dCxcbiAgICAgICAgICAgICAgICByZXNwb25zZS5kYXRhXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIHRocm93IG5ldyBGYWFzdEVycm9yKGVyciwgYHdoZW4gaW52b2tpbmcgZ29vZ2xlIGNsb3VkIGZ1bmN0aW9uYCk7XG4gICAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiBpbnZva2UoXG4gICAgc3RhdGU6IEdvb2dsZVN0YXRlLFxuICAgIGNhbGw6IEZ1bmN0aW9uQ2FsbCxcbiAgICBjYW5jZWw6IFByb21pc2U8dm9pZD5cbik6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHsgb3B0aW9ucywgcmVzb3VyY2VzLCBzZXJ2aWNlcywgdXJsLCBtZXRyaWNzIH0gPSBzdGF0ZTtcbiAgICBzd2l0Y2ggKG9wdGlvbnMubW9kZSkge1xuICAgICAgICBjYXNlIFwiYXV0b1wiOlxuICAgICAgICBjYXNlIFwiaHR0cHNcIjpcbiAgICAgICAgICAgIHJldHVybiBjYWxsRnVuY3Rpb25IdHRwcyh1cmwhLCBjYWxsLCBtZXRyaWNzLCBjYW5jZWwpO1xuICAgICAgICBjYXNlIFwicXVldWVcIjpcbiAgICAgICAgICAgIGNvbnN0IHsgcmVxdWVzdFF1ZXVlVG9waWMgfSA9IHJlc291cmNlcztcbiAgICAgICAgICAgIGNvbnN0IHsgcHVic3ViIH0gPSBzZXJ2aWNlcztcbiAgICAgICAgICAgIGNvbnN0IHNlcmlhbGl6ZWQgPSBzZXJpYWxpemUoY2FsbCk7XG4gICAgICAgICAgICByZXR1cm4gcHVibGlzaFB1YlN1YihwdWJzdWIsIHJlcXVlc3RRdWV1ZVRvcGljISwgc2VyaWFsaXplZCk7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBwb2xsKHN0YXRlOiBHb29nbGVTdGF0ZSwgY2FuY2VsOiBQcm9taXNlPHZvaWQ+KTogUHJvbWlzZTxQb2xsUmVzdWx0PiB7XG4gICAgcmV0dXJuIHJlY2VpdmVNZXNzYWdlcyhcbiAgICAgICAgc3RhdGUuc2VydmljZXMucHVic3ViLFxuICAgICAgICBzdGF0ZS5yZXNvdXJjZXMucmVzcG9uc2VTdWJzY3JpcHRpb24hLFxuICAgICAgICBzdGF0ZS5tZXRyaWNzLFxuICAgICAgICBjYW5jZWxcbiAgICApO1xufVxuXG5mdW5jdGlvbiByZXNwb25zZVF1ZXVlSWQoc3RhdGU6IEdvb2dsZVN0YXRlKSB7XG4gICAgcmV0dXJuIHN0YXRlLnJlc291cmNlcy5yZXNwb25zZVF1ZXVlVG9waWMhO1xufVxuXG5hc3luYyBmdW5jdGlvbiBkZWxldGVSZXNvdXJjZXMoXG4gICAgc2VydmljZXM6IEdvb2dsZVNlcnZpY2VzLFxuICAgIHJlc291cmNlczogR29vZ2xlUmVzb3VyY2VzLFxuICAgIG91dHB1dDogKG1zZzogc3RyaW5nKSA9PiB2b2lkID0gbG9nLmluZm9cbikge1xuICAgIGNvbnN0IHtcbiAgICAgICAgdHJhbXBvbGluZSxcbiAgICAgICAgcmVxdWVzdFF1ZXVlVG9waWMsXG4gICAgICAgIHJlcXVlc3RTdWJzY3JpcHRpb24sXG4gICAgICAgIHJlc3BvbnNlU3Vic2NyaXB0aW9uLFxuICAgICAgICByZXNwb25zZVF1ZXVlVG9waWMsXG4gICAgICAgIHJlZ2lvbixcbiAgICAgICAgLi4ucmVzdFxuICAgIH0gPSByZXNvdXJjZXM7XG4gICAgY29uc3QgX2V4aGF1c3RpdmVDaGVjazogUmVxdWlyZWQ8dHlwZW9mIHJlc3Q+ID0ge307XG4gICAgY29uc3QgeyBjbG91ZEZ1bmN0aW9ucywgcHVic3ViIH0gPSBzZXJ2aWNlcztcblxuICAgIC8vIFdlIGRlbGliZXJhdGVseSByZXRocm93IHRyYW5zaWVudCBlcnJvcnMgaGVyZSwgc28gb25seSBpZiBhbGwgcHJpb3JcbiAgICAvLyBkZWxldGVzIHN1Y2NlZWQgZG8gd2UgcHJvY2VlZC4gSWYgdGhlcmUncyBhIHRyYW5zaWVudCBlcnJvciB0aGVuIGZ1dHVyZVxuICAgIC8vIGdhcmJhZ2UgY29sbGVjdGlvbiB3aWxsIGNsZWFuIHVwLiBUaGUgb3JkZXIgaXMgaW1wb3J0YW50OyB0aGUgZnVuY3Rpb25cbiAgICAvLyBpdHNlbGYgbXVzdCBiZSBkZWxldGVkIGxhc3QuXG4gICAgY29uc3QgY2hlY2sgPSBhc3luYyA8VD4ocmVxdWVzdDogUHJvbWlzZTxUPikgPT4ge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgcmVxdWVzdDtcbiAgICAgICAgfSBjYXRjaCAoZXJyOiBhbnkpIHtcbiAgICAgICAgICAgIC8qIGlzdGFuYnVsIGlnbm9yZSBuZXh0ICAqL1xuICAgICAgICAgICAgaWYgKGVyci5tZXNzYWdlLm1hdGNoKC9SZXNvdXJjZSBub3QgZm91bmQvKSkge1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRocm93IGVycjtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICBpZiAocmVzcG9uc2VTdWJzY3JpcHRpb24pIHtcbiAgICAgICAgYXdhaXQgY2hlY2soXG4gICAgICAgICAgICBwdWJzdWIucHJvamVjdHMuc3Vic2NyaXB0aW9ucy5kZWxldGUoeyBzdWJzY3JpcHRpb246IHJlc3BvbnNlU3Vic2NyaXB0aW9uIH0pXG4gICAgICAgICk7XG4gICAgICAgIG91dHB1dChgRGVsZXRlZCByZXNwb25zZSBzdWJzY3JpcHRpb246ICR7cmVzcG9uc2VTdWJzY3JpcHRpb259YCk7XG4gICAgfVxuICAgIGlmIChyZXNwb25zZVF1ZXVlVG9waWMpIHtcbiAgICAgICAgYXdhaXQgY2hlY2socHVic3ViLnByb2plY3RzLnRvcGljcy5kZWxldGUoeyB0b3BpYzogcmVzcG9uc2VRdWV1ZVRvcGljIH0pKTtcbiAgICAgICAgb3V0cHV0KGBEZWxldGVkIHJlc3BvbnNlIHF1ZXVlIHRvcGljOiAke3Jlc3BvbnNlUXVldWVUb3BpY31gKTtcbiAgICB9XG4gICAgaWYgKHJlcXVlc3RTdWJzY3JpcHRpb24pIHtcbiAgICAgICAgYXdhaXQgY2hlY2soXG4gICAgICAgICAgICBwdWJzdWIucHJvamVjdHMuc3Vic2NyaXB0aW9ucy5kZWxldGUoeyBzdWJzY3JpcHRpb246IHJlcXVlc3RTdWJzY3JpcHRpb24gfSlcbiAgICAgICAgKTtcbiAgICAgICAgb3V0cHV0KGBEZWxldGVkIHJlc3BvbnNlIHN1YnNjcmlwdGlvbjogJHtyZXF1ZXN0U3Vic2NyaXB0aW9ufWApO1xuICAgIH1cbiAgICBpZiAocmVxdWVzdFF1ZXVlVG9waWMpIHtcbiAgICAgICAgYXdhaXQgY2hlY2socHVic3ViLnByb2plY3RzLnRvcGljcy5kZWxldGUoeyB0b3BpYzogcmVxdWVzdFF1ZXVlVG9waWMgfSkpO1xuICAgICAgICBvdXRwdXQoYERlbGV0ZWQgcmVxdWVzdCBxdWV1ZSB0b3BpYzogJHtyZXF1ZXN0UXVldWVUb3BpY31gKTtcbiAgICB9XG4gICAgaWYgKHRyYW1wb2xpbmUpIHtcbiAgICAgICAgYXdhaXQgY2hlY2soZGVsZXRlRnVuY3Rpb24oY2xvdWRGdW5jdGlvbnMsIHRyYW1wb2xpbmUpKTtcbiAgICAgICAgb3V0cHV0KGBEZWxldGVkIGZ1bmN0aW9uICR7dHJhbXBvbGluZX1gKTtcbiAgICB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbGVhbnVwKHN0YXRlOiBHb29nbGVTdGF0ZSwgb3B0aW9uczogQ2xlYW51cE9wdGlvbnMpIHtcbiAgICBsb2cuaW5mbyhgZ29vZ2xlIGNsZWFudXAgc3RhcnRpbmcuYCk7XG4gICAgaWYgKHN0YXRlLmdjUHJvbWlzZSkge1xuICAgICAgICBsb2cuaW5mbyhgV2FpdGluZyBmb3IgZ2FyYmFnZSBjb2xsZWN0aW9uLi4uYCk7XG4gICAgICAgIGF3YWl0IHN0YXRlLmdjUHJvbWlzZTtcbiAgICAgICAgbG9nLmluZm8oYEdhcmJhZ2UgY29sbGVjdGlvbiBkb25lLmApO1xuICAgIH1cblxuICAgIGlmIChvcHRpb25zLmRlbGV0ZVJlc291cmNlcykge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgZGVsZXRlUmVzb3VyY2VzKHN0YXRlLnNlcnZpY2VzLCBzdGF0ZS5yZXNvdXJjZXMpO1xuICAgICAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEZhYXN0RXJyb3IoZXJyLCBcImRlbGV0ZSByZXNvdXJjZXMgZmFpbGVkXCIpO1xuICAgICAgICB9XG4gICAgfVxuICAgIGxvZy5pbmZvKGBnb29nbGUgY2xlYW51cCBkb25lLmApO1xufVxuXG5sZXQgZ2FyYmFnZUNvbGxlY3RvclJ1bm5pbmcgPSBmYWxzZTtcblxuYXN5bmMgZnVuY3Rpb24gY29sbGVjdEdhcmJhZ2UoXG4gICAgZ2NXb3JrZXI6IHR5cGVvZiBkZWZhdWx0R2NXb3JrZXIsXG4gICAgc2VydmljZXM6IEdvb2dsZVNlcnZpY2VzLFxuICAgIHByb2o6IHN0cmluZyxcbiAgICByZXRlbnRpb25JbkRheXM6IG51bWJlclxuKSB7XG4gICAgaWYgKGdjV29ya2VyID09PSBkZWZhdWx0R2NXb3JrZXIpIHtcbiAgICAgICAgaWYgKGdhcmJhZ2VDb2xsZWN0b3JSdW5uaW5nKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgZ2FyYmFnZUNvbGxlY3RvclJ1bm5pbmcgPSB0cnVlO1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgICBjb25zdCB7IGNsb3VkRnVuY3Rpb25zIH0gPSBzZXJ2aWNlcztcblxuICAgICAgICBsZXQgcGFnZVRva2VuOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG5cbiAgICAgICAgbGV0IHByb21pc2VzID0gW107XG4gICAgICAgIGNvbnN0IHNjaGVkdWxlRGVsZXRlUmVzb3VyY2VzID0gdGhyb3R0bGUoXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgY29uY3VycmVuY3k6IDUsXG4gICAgICAgICAgICAgICAgcmF0ZTogNSxcbiAgICAgICAgICAgICAgICBidXJzdDogMlxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGFzeW5jIChcbiAgICAgICAgICAgICAgICBnU2VydmljZXM6IEdvb2dsZVNlcnZpY2VzLFxuICAgICAgICAgICAgICAgIGZuOiBDbG91ZEZ1bmN0aW9ucy5TY2hlbWEkQ2xvdWRGdW5jdGlvblxuICAgICAgICAgICAgKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgeyByZWdpb24sIG5hbWUsIHByb2plY3QgfSA9IHBhcnNlRnVuY3Rpb25OYW1lKGZuLm5hbWUhKSE7XG5cbiAgICAgICAgICAgICAgICBjb25zdCByZXNvdXJjZXM6IEdvb2dsZVJlc291cmNlcyA9IHtcbiAgICAgICAgICAgICAgICAgICAgcmVnaW9uLFxuICAgICAgICAgICAgICAgICAgICB0cmFtcG9saW5lOiBmbi5uYW1lISxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWVzdFF1ZXVlVG9waWM6IGdldFJlcXVlc3RRdWV1ZVRvcGljKHByb2plY3QsIG5hbWUpLFxuICAgICAgICAgICAgICAgICAgICByZXF1ZXN0U3Vic2NyaXB0aW9uOiBnZXRSZXF1ZXN0U3Vic2NyaXB0aW9uKHByb2plY3QsIG5hbWUsIHJlZ2lvbiksXG4gICAgICAgICAgICAgICAgICAgIHJlc3BvbnNlUXVldWVUb3BpYzogZ2V0UmVzcG9uc2VRdWV1ZVRvcGljKHByb2plY3QsIG5hbWUpLFxuICAgICAgICAgICAgICAgICAgICByZXNwb25zZVN1YnNjcmlwdGlvbjogZ2V0UmVzcG9uc2VTdWJzY3JpcHRpb24ocHJvamVjdCwgbmFtZSlcbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIGF3YWl0IGdjV29ya2VyKHJlc291cmNlcywgZ1NlcnZpY2VzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgKTtcblxuICAgICAgICBjb25zdCBmblBhdHRlcm4gPSBuZXcgUmVnRXhwKGAvZnVuY3Rpb25zL2ZhYXN0LSR7dXVpZHY0UGF0dGVybn0kYCk7XG4gICAgICAgIGRvIHtcbiAgICAgICAgICAgIGNvbnN0IGZ1bmNMaXN0UmVzcG9uc2UgPVxuICAgICAgICAgICAgICAgIGF3YWl0IGNsb3VkRnVuY3Rpb25zLnByb2plY3RzLmxvY2F0aW9ucy5mdW5jdGlvbnMubGlzdCh7XG4gICAgICAgICAgICAgICAgICAgIHBhcmVudDogYHByb2plY3RzLyR7cHJvan0vbG9jYXRpb25zLy1gLFxuICAgICAgICAgICAgICAgICAgICBwYWdlVG9rZW5cbiAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgcGFnZVRva2VuID0gZnVuY0xpc3RSZXNwb25zZS5kYXRhLm5leHRQYWdlVG9rZW4gPz8gdW5kZWZpbmVkO1xuICAgICAgICAgICAgY29uc3QgZ2FyYmFnZUZ1bmN0aW9ucyA9IChmdW5jTGlzdFJlc3BvbnNlLmRhdGEuZnVuY3Rpb25zIHx8IFtdKVxuICAgICAgICAgICAgICAgIC5maWx0ZXIoZm4gPT4gaGFzRXhwaXJlZChmbi51cGRhdGVUaW1lLCByZXRlbnRpb25JbkRheXMpKVxuICAgICAgICAgICAgICAgIC5maWx0ZXIoZm4gPT4gZm4ubmFtZSEubWF0Y2goZm5QYXR0ZXJuKSk7XG5cbiAgICAgICAgICAgIHByb21pc2VzID0gZ2FyYmFnZUZ1bmN0aW9ucy5tYXAoZm4gPT4gc2NoZWR1bGVEZWxldGVSZXNvdXJjZXMoc2VydmljZXMsIGZuKSk7XG4gICAgICAgIH0gd2hpbGUgKHBhZ2VUb2tlbik7XG5cbiAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwocHJvbWlzZXMpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICAgIGlmIChnY1dvcmtlciA9PT0gZGVmYXVsdEdjV29ya2VyKSB7XG4gICAgICAgICAgICBnYXJiYWdlQ29sbGVjdG9yUnVubmluZyA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgfVxufVxuXG5mdW5jdGlvbiBwYXJzZUZ1bmN0aW9uTmFtZShwYXRoOiBzdHJpbmcpIHtcbiAgICBjb25zdCBtYXRjaCA9IHBhdGgubWF0Y2goL15wcm9qZWN0c1xcLyguKilcXC9sb2NhdGlvbnNcXC8oLiopXFwvZnVuY3Rpb25zXFwvKC4qKSQvKTtcbiAgICByZXR1cm4gbWF0Y2ggJiYgeyBwcm9qZWN0OiBtYXRjaFsxXSwgcmVnaW9uOiBtYXRjaFsyXSwgbmFtZTogbWF0Y2hbM10gfTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gdXBsb2FkWmlwKHVybDogc3RyaW5nLCB6aXBTdHJlYW06IE5vZGVKUy5SZWFkYWJsZVN0cmVhbSkge1xuICAgIGNvbnN0IGNvbmZpZzogR2F4aW9zT3B0aW9ucyA9IHtcbiAgICAgICAgbWV0aG9kOiBcIlBVVFwiLFxuICAgICAgICB1cmwsXG4gICAgICAgIGJvZHk6IHppcFN0cmVhbSxcbiAgICAgICAgaGVhZGVyczoge1xuICAgICAgICAgICAgXCJjb250ZW50LXR5cGVcIjogXCJhcHBsaWNhdGlvbi96aXBcIixcbiAgICAgICAgICAgIFwieC1nb29nLWNvbnRlbnQtbGVuZ3RoLXJhbmdlXCI6IFwiMCwxMDQ4NTc2MDBcIlxuICAgICAgICB9XG4gICAgfTtcbiAgICByZXR1cm4gZ2F4aW9zLnJlcXVlc3QoY29uZmlnKTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdvb2dsZVBhY2tlcihcbiAgICBmdW5jdGlvbk1vZHVsZTogc3RyaW5nLFxuICAgIG9wdGlvbnM6IENvbW1vbk9wdGlvbnMsXG4gICAgd3JhcHBlck9wdGlvbnM6IFdyYXBwZXJPcHRpb25zLFxuICAgIEZ1bmN0aW9uTmFtZTogc3RyaW5nXG4pOiBQcm9taXNlPFBhY2tlclJlc3VsdD4ge1xuICAgIGNvbnN0IHsgbW9kZSB9ID0gb3B0aW9ucztcbiAgICBjb25zdCB0cmFtcG9saW5lTW9kdWxlID1cbiAgICAgICAgbW9kZSA9PT0gXCJxdWV1ZVwiID8gZ29vZ2xlVHJhbXBvbGluZVF1ZXVlIDogZ29vZ2xlVHJhbXBvbGluZUh0dHBzO1xuICAgIHJldHVybiBwYWNrZXIoXG4gICAgICAgIHRyYW1wb2xpbmVNb2R1bGUsXG4gICAgICAgIGZ1bmN0aW9uTW9kdWxlLFxuICAgICAgICBvcHRpb25zLFxuICAgICAgICB3cmFwcGVyT3B0aW9ucyxcbiAgICAgICAgRnVuY3Rpb25OYW1lXG4gICAgKTtcbn1cblxubGV0IGdldEdvb2dsZVByaWNlOlxuICAgIHwgdW5kZWZpbmVkXG4gICAgfCAoKFxuICAgICAgICAgIHJlZ2lvbjogc3RyaW5nLFxuICAgICAgICAgIHNlcnZpY2VOYW1lOiBzdHJpbmcsXG4gICAgICAgICAgZGVzY3JpcHRpb246IHN0cmluZyxcbiAgICAgICAgICBjb252ZXJzaW9uRmFjdG9yOiBudW1iZXJcbiAgICAgICkgPT4gUHJvbWlzZTxudW1iZXI+KTtcblxuZnVuY3Rpb24gZW5zdXJlR29vZ2xlUHJpY2VDYWNoZShjbG91ZEJpbGxpbmc6IENsb3VkQmlsbGluZy5DbG91ZGJpbGxpbmcpIHtcbiAgICBpZiAoZ2V0R29vZ2xlUHJpY2UpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBnZXRHb29nbGVQcmljZSA9IHRocm90dGxlKFxuICAgICAgICB7XG4gICAgICAgICAgICBjb25jdXJyZW5jeTogMSxcbiAgICAgICAgICAgIHJhdGU6IDMsXG4gICAgICAgICAgICBtZW1vaXplOiB0cnVlLFxuICAgICAgICAgICAgY2FjaGU6IGNhY2hlcy5nb29nbGVQcmljZXNcbiAgICAgICAgfSxcbiAgICAgICAgYXN5bmMgKFxuICAgICAgICAgICAgcmVnaW9uOiBzdHJpbmcsXG4gICAgICAgICAgICBzZXJ2aWNlTmFtZTogc3RyaW5nLFxuICAgICAgICAgICAgZGVzY3JpcHRpb246IHN0cmluZyxcbiAgICAgICAgICAgIGNvbnZlcnNpb25GYWN0b3I6IG51bWJlclxuICAgICAgICApID0+IHtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgY29uc3Qgc2t1c1Jlc3BvbnNlID0gYXdhaXQgY2xvdWRCaWxsaW5nLnNlcnZpY2VzLnNrdXMubGlzdCh7XG4gICAgICAgICAgICAgICAgICAgIHBhcmVudDogc2VydmljZU5hbWVcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICBjb25zdCB7IHNrdXMgPSBbXSB9ID0gc2t1c1Jlc3BvbnNlLmRhdGE7XG4gICAgICAgICAgICAgICAgY29uc3QgbWF0Y2hpbmdTa3VzID0gc2t1cy5maWx0ZXIoc2t1ID0+IHNrdS5kZXNjcmlwdGlvbiA9PT0gZGVzY3JpcHRpb24pO1xuICAgICAgICAgICAgICAgIGxvZy5wcm92aWRlcihcbiAgICAgICAgICAgICAgICAgICAgYG1hdGNoaW5nIFNLVXM6ICR7dXRpbC5pbnNwZWN0KG1hdGNoaW5nU2t1cywgeyBkZXB0aDogbnVsbCB9KX1gXG4gICAgICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgICAgIGNvbnN0IHJlZ2lvbk9yR2xvYmFsU2t1ID1cbiAgICAgICAgICAgICAgICAgICAgbWF0Y2hpbmdTa3VzLmZpbmQoc2t1ID0+XG4gICAgICAgICAgICAgICAgICAgICAgICBza3Uuc2VydmljZVJlZ2lvbnMhLmZpbmQociA9PiByID09PSByZWdpb24pXG4gICAgICAgICAgICAgICAgICAgICkgPz9cbiAgICAgICAgICAgICAgICAgICAgbWF0Y2hpbmdTa3VzLmZpbmQoc2t1ID0+XG4gICAgICAgICAgICAgICAgICAgICAgICBza3Uuc2VydmljZVJlZ2lvbnMhLmZpbmQociA9PiByID09PSBcImdsb2JhbFwiKVxuICAgICAgICAgICAgICAgICAgICApO1xuXG4gICAgICAgICAgICAgICAgY29uc3QgcGV4cCA9IHJlZ2lvbk9yR2xvYmFsU2t1IS5wcmljaW5nSW5mbyFbMF0ucHJpY2luZ0V4cHJlc3Npb24hO1xuICAgICAgICAgICAgICAgIGNvbnN0IHByaWNlcyA9IHBleHAudGllcmVkUmF0ZXMhLm1hcChcbiAgICAgICAgICAgICAgICAgICAgcmF0ZSA9PlxuICAgICAgICAgICAgICAgICAgICAgICAgTnVtYmVyKHJhdGUudW5pdFByaWNlIS51bml0cyA/PyBcIjBcIikgK1xuICAgICAgICAgICAgICAgICAgICAgICAgcmF0ZS51bml0UHJpY2UhLm5hbm9zISAvIDFlOVxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgY29uc3QgcHJpY2UgPVxuICAgICAgICAgICAgICAgICAgICBNYXRoLm1heCguLi5wcmljZXMpICpcbiAgICAgICAgICAgICAgICAgICAgKGNvbnZlcnNpb25GYWN0b3IgLyBwZXhwLmJhc2VVbml0Q29udmVyc2lvbkZhY3RvciEpO1xuICAgICAgICAgICAgICAgIGxvZy5wcm92aWRlcihcbiAgICAgICAgICAgICAgICAgICAgYEZvdW5kIHByaWNlIGZvciAke3NlcnZpY2VOYW1lfSwgJHtkZXNjcmlwdGlvbn0sICR7cmVnaW9ufTogJHtwcmljZX1gXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gcHJpY2U7XG4gICAgICAgICAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBGYWFzdEVycm9yKFxuICAgICAgICAgICAgICAgICAgICBlcnIsXG4gICAgICAgICAgICAgICAgICAgIGBmYWlsZWQgdG8gZ2V0IGdvb2dsZSBwcmljaW5nIGZvciBcIiR7ZGVzY3JpcHRpb259XCJgXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICk7XG59XG5cbmxldCBnb29nbGVTZXJ2aWNlczogY2xvdWRiaWxsaW5nX3YxLlNjaGVtYSRTZXJ2aWNlW10gfCB1bmRlZmluZWQ7XG5cbmNvbnN0IGxpc3RHb29nbGVTZXJ2aWNlcyA9IHRocm90dGxlKFxuICAgIHsgY29uY3VycmVuY3k6IDEgfSxcbiAgICBhc3luYyAoY2xvdWRCaWxsaW5nOiBDbG91ZEJpbGxpbmcuQ2xvdWRiaWxsaW5nKSA9PiB7XG4gICAgICAgIGlmIChnb29nbGVTZXJ2aWNlcykge1xuICAgICAgICAgICAgcmV0dXJuIGdvb2dsZVNlcnZpY2VzO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgY2xvdWRCaWxsaW5nLnNlcnZpY2VzLmxpc3QoKTtcbiAgICAgICAgZ29vZ2xlU2VydmljZXMgPSByZXNwb25zZS5kYXRhLnNlcnZpY2VzITtcbiAgICAgICAgcmV0dXJuIGdvb2dsZVNlcnZpY2VzO1xuICAgIH1cbik7XG5cbmFzeW5jIGZ1bmN0aW9uIGdldEdvb2dsZUNsb3VkRnVuY3Rpb25zUHJpY2luZyhcbiAgICBjbG91ZEJpbGxpbmc6IENsb3VkQmlsbGluZy5DbG91ZGJpbGxpbmcsXG4gICAgcmVnaW9uOiBzdHJpbmdcbik6IFByb21pc2U8R29vZ2xlQ2xvdWRQcmljaW5nPiB7XG4gICAgY29uc3Qgc2VydmljZXMgPSBhd2FpdCBsaXN0R29vZ2xlU2VydmljZXMoY2xvdWRCaWxsaW5nKTtcbiAgICBlbnN1cmVHb29nbGVQcmljZUNhY2hlKGNsb3VkQmlsbGluZyk7XG5cbiAgICBjb25zdCBnZXRQcmljaW5nID0gKFxuICAgICAgICBzZXJ2aWNlTmFtZTogc3RyaW5nLFxuICAgICAgICBkZXNjcmlwdGlvbjogc3RyaW5nLFxuICAgICAgICBjb252ZXJzaW9uRmFjdG9yOiBudW1iZXIgPSAxXG4gICAgKSA9PiB7XG4gICAgICAgIGNvbnN0IHNlcnZpY2UgPSBzZXJ2aWNlcy5maW5kKHMgPT4gcy5kaXNwbGF5TmFtZSA9PT0gc2VydmljZU5hbWUpITtcbiAgICAgICAgcmV0dXJuIGdldEdvb2dsZVByaWNlIShyZWdpb24sIHNlcnZpY2UubmFtZSEsIGRlc2NyaXB0aW9uLCBjb252ZXJzaW9uRmFjdG9yKTtcbiAgICB9O1xuXG4gICAgcmV0dXJuIHtcbiAgICAgICAgcGVySW52b2NhdGlvbjogYXdhaXQgZ2V0UHJpY2luZyhcIkNsb3VkIEZ1bmN0aW9uc1wiLCBcIkludm9jYXRpb25zXCIpLFxuICAgICAgICBwZXJHaHpTZWNvbmQ6IGF3YWl0IGdldFByaWNpbmcoXCJDbG91ZCBGdW5jdGlvbnNcIiwgXCJDUFUgVGltZVwiKSxcbiAgICAgICAgcGVyR2JTZWNvbmQ6IGF3YWl0IGdldFByaWNpbmcoXCJDbG91ZCBGdW5jdGlvbnNcIiwgXCJNZW1vcnkgVGltZVwiLCAyICoqIDMwKSxcbiAgICAgICAgcGVyR2JPdXRib3VuZERhdGE6IGF3YWl0IGdldFByaWNpbmcoXG4gICAgICAgICAgICBcIkNsb3VkIEZ1bmN0aW9uc1wiLFxuICAgICAgICAgICAgYE5ldHdvcmsgRWdyZXNzIGZyb20gJHtyZWdpb259YCxcbiAgICAgICAgICAgIDIgKiogMzBcbiAgICAgICAgKSxcbiAgICAgICAgcGVyR2JQdWJTdWI6IGF3YWl0IGdldFByaWNpbmcoXCJDbG91ZCBQdWIvU3ViXCIsIFwiTWVzc2FnZSBEZWxpdmVyeSBCYXNpY1wiLCAyICoqIDMwKVxuICAgIH07XG59XG5cbi8vIGh0dHBzOi8vY2xvdWQuZ29vZ2xlLmNvbS9mdW5jdGlvbnMvcHJpY2luZ1xuY29uc3QgZ2NmUHJvdmlzb25hYmxlTWVtb3J5VGFibGU6IHsgW21lbTogbnVtYmVyXTogbnVtYmVyIH0gPSB7XG4gICAgMTI4OiAwLjIsXG4gICAgMjU2OiAwLjQsXG4gICAgNTEyOiAwLjgsXG4gICAgMTAyNDogMS40LFxuICAgIDIwNDg6IDIuNFxufTtcblxuYXN5bmMgZnVuY3Rpb24gY29zdFNuYXBzaG90KFxuICAgIHN0YXRlOiBHb29nbGVTdGF0ZSxcbiAgICBzdGF0czogRnVuY3Rpb25TdGF0c1xuKTogUHJvbWlzZTxDb3N0U25hcHNob3Q+IHtcbiAgICBjb25zdCBjb3N0cyA9IG5ldyBDb3N0U25hcHNob3QoXCJnb29nbGVcIiwgc3RhdGUub3B0aW9ucywgc3RhdHMpO1xuICAgIGNvbnN0IHsgbWVtb3J5U2l6ZSA9IGRlZmF1bHRzLm1lbW9yeVNpemUgfSA9IHN0YXRlLm9wdGlvbnM7XG4gICAgY29uc3QgcHJvdmlzaW9uYWJsZVNpemVzID0ga2V5c09mKGdjZlByb3Zpc29uYWJsZU1lbW9yeVRhYmxlKVxuICAgICAgICAubWFwKG4gPT4gTnVtYmVyKG4pKVxuICAgICAgICAuc29ydCgoYSwgYikgPT4gYSAtIGIpO1xuICAgIGNvbnN0IHByb3Zpc2lvbmVkTWIgPSBwcm92aXNpb25hYmxlU2l6ZXMuZmluZChzaXplID0+IG1lbW9yeVNpemUgPD0gc2l6ZSk7XG4gICAgaWYgKCFwcm92aXNpb25lZE1iKSB7XG4gICAgICAgIGxvZy53YXJuKFxuICAgICAgICAgICAgYENvdWxkIG5vdCBkZXRlcm1pbmUgcHJvdmlzaW9uZWQgbWVtb3J5IG9yIENQVSBmb3IgcmVxdWVzdGVkIG1lbW9yeSBzaXplICR7bWVtb3J5U2l6ZX1gXG4gICAgICAgICk7XG4gICAgfVxuICAgIGNvbnN0IHByb3Zpc2lvbmVkR2h6ID0gZ2NmUHJvdmlzb25hYmxlTWVtb3J5VGFibGVbcHJvdmlzaW9uZWRNYiFdO1xuICAgIGNvbnN0IGJpbGxlZFRpbWVTdGF0cyA9IHN0YXRzLmVzdGltYXRlZEJpbGxlZFRpbWU7XG4gICAgY29uc3Qgc2Vjb25kcyA9IChiaWxsZWRUaW1lU3RhdHMubWVhbiAvIDEwMDApICogYmlsbGVkVGltZVN0YXRzLnNhbXBsZXM7XG5cbiAgICBjb25zdCB7IHJlZ2lvbiB9ID0gc3RhdGUucmVzb3VyY2VzO1xuICAgIGNvbnN0IHByaWNlcyA9IGF3YWl0IGdldEdvb2dsZUNsb3VkRnVuY3Rpb25zUHJpY2luZyhcbiAgICAgICAgc3RhdGUuc2VydmljZXMuY2xvdWRCaWxsaW5nLFxuICAgICAgICByZWdpb25cbiAgICApO1xuXG4gICAgY29uc3QgcHJvdmlzaW9uZWRHYiA9IHByb3Zpc2lvbmVkTWIhIC8gMTAyNDtcbiAgICBjb25zdCBmdW5jdGlvbkNhbGxEdXJhdGlvbiA9IG5ldyBDb3N0TWV0cmljKHtcbiAgICAgICAgbmFtZTogXCJmdW5jdGlvbkNhbGxEdXJhdGlvblwiLFxuICAgICAgICBwcmljaW5nOlxuICAgICAgICAgICAgcHJpY2VzLnBlckdiU2Vjb25kICogcHJvdmlzaW9uZWRHYiArIHByaWNlcy5wZXJHaHpTZWNvbmQgKiBwcm92aXNpb25lZEdoeixcbiAgICAgICAgdW5pdDogXCJzZWNvbmRcIixcbiAgICAgICAgbWVhc3VyZWQ6IHNlY29uZHMsXG4gICAgICAgIGNvbW1lbnQ6IGBodHRwczovL2Nsb3VkLmdvb2dsZS5jb20vZnVuY3Rpb25zL3ByaWNpbmcjY29tcHV0ZV90aW1lICgke3Byb3Zpc2lvbmVkTWJ9IE1CLCAke3Byb3Zpc2lvbmVkR2h6fSBHSHopYFxuICAgIH0pO1xuICAgIGNvc3RzLnB1c2goZnVuY3Rpb25DYWxsRHVyYXRpb24pO1xuXG4gICAgY29uc3QgZnVuY3Rpb25DYWxsUmVxdWVzdHMgPSBuZXcgQ29zdE1ldHJpYyh7XG4gICAgICAgIG5hbWU6IFwiZnVuY3Rpb25DYWxsUmVxdWVzdHNcIixcbiAgICAgICAgcHJpY2luZzogcHJpY2VzLnBlckludm9jYXRpb24sXG4gICAgICAgIG1lYXN1cmVkOiBzdGF0cy5pbnZvY2F0aW9ucyxcbiAgICAgICAgdW5pdDogXCJyZXF1ZXN0XCIsXG4gICAgICAgIGNvbW1lbnQ6IFwiaHR0cHM6Ly9jbG91ZC5nb29nbGUuY29tL2Z1bmN0aW9ucy9wcmljaW5nI2ludm9jYXRpb25zXCJcbiAgICB9KTtcbiAgICBjb3N0cy5wdXNoKGZ1bmN0aW9uQ2FsbFJlcXVlc3RzKTtcblxuICAgIGNvbnN0IG91dGJvdW5kRGF0YVRyYW5zZmVyID0gbmV3IENvc3RNZXRyaWMoe1xuICAgICAgICBuYW1lOiBcIm91dGJvdW5kRGF0YVRyYW5zZmVyXCIsXG4gICAgICAgIHByaWNpbmc6IHByaWNlcy5wZXJHYk91dGJvdW5kRGF0YSxcbiAgICAgICAgbWVhc3VyZWQ6IHN0YXRlLm1ldHJpY3Mub3V0Ym91bmRCeXRlcyAvIDIgKiogMzAsXG4gICAgICAgIHVuaXQ6IFwiR0JcIixcbiAgICAgICAgY29tbWVudDogXCJodHRwczovL2Nsb3VkLmdvb2dsZS5jb20vZnVuY3Rpb25zL3ByaWNpbmcjbmV0d29ya2luZ1wiXG4gICAgfSk7XG4gICAgY29zdHMucHVzaChvdXRib3VuZERhdGFUcmFuc2Zlcik7XG5cbiAgICBjb25zdCBwdWJzdWIgPSBuZXcgQ29zdE1ldHJpYyh7XG4gICAgICAgIG5hbWU6IFwicHVic3ViXCIsXG4gICAgICAgIHByaWNpbmc6IHByaWNlcy5wZXJHYlB1YlN1YixcbiAgICAgICAgbWVhc3VyZWQ6IHN0YXRlLm1ldHJpY3MucHViU3ViQnl0ZXMgLyAyICoqIDMwLFxuICAgICAgICB1bml0OiBcIkdCXCIsXG4gICAgICAgIGNvbW1lbnQ6IFwiaHR0cHM6Ly9jbG91ZC5nb29nbGUuY29tL3B1YnN1Yi9wcmljaW5nXCJcbiAgICB9KTtcbiAgICBjb3N0cy5wdXNoKHB1YnN1Yik7XG5cbiAgICByZXR1cm4gY29zdHM7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsb2dVcmwoc3RhdGU6IEdvb2dsZVN0YXRlKSB7XG4gICAgY29uc3QgeyBwcm9qZWN0LCBmdW5jdGlvbk5hbWUgfSA9IHN0YXRlO1xuICAgIHJldHVybiBgaHR0cHM6Ly9jb25zb2xlLmNsb3VkLmdvb2dsZS5jb20vbG9ncy92aWV3ZXI/cHJvamVjdD0ke3Byb2plY3R9JnJlc291cmNlPWNsb3VkX2Z1bmN0aW9uJTJGZnVuY3Rpb25fbmFtZSUyRiR7ZnVuY3Rpb25OYW1lfWA7XG59XG4iXX0= |
\ | No newline at end of file |