faastjs
Version:
Serverless batch computing made simple.
236 lines • 29.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const ava_1 = tslib_1.__importDefault(require("ava"));
const fs_extra_1 = require("fs-extra");
const url_1 = require("url");
const util_1 = require("util");
const index_1 = require("../index");
const funcs = tslib_1.__importStar(require("./fixtures/functions"));
const util_2 = require("./fixtures/util");
const error_1 = require("../src/error");
async function testCleanup(t, options) {
const m = await (0, index_1.faastLocal)(funcs, {
gc: "off",
...options
});
let done = 0;
m.functions
.hello("there")
.then(_ => done++)
.catch(_ => { });
m.functions
.sleep(1000)
.then(_ => done++)
.catch(_ => { });
await m.cleanup();
t.is(done, 0);
}
async function testOrder(t, options) {
const faastModule = await (0, index_1.faastLocal)(funcs, {
gc: "off",
...options
});
t.plan(2);
const a = faastModule.functions.emptyReject();
const b = faastModule.functions.sleep(0);
t.is(await b, undefined);
try {
await a;
}
catch (err) {
t.is(err, undefined);
}
finally {
await faastModule.cleanup();
}
}
async function testConcurrency(t, { options, maxConcurrency, expectedConcurrency }) {
const faastModule = await (0, index_1.faastLocal)(funcs, {
...options,
gc: "off",
concurrency: maxConcurrency
});
try {
const N = maxConcurrency * 2;
const promises = [];
for (let i = 0; i < N; i++) {
promises.push(faastModule.functions.spin(2000));
}
const timings = await Promise.all(promises);
t.is((0, util_2.measureConcurrency)(timings), expectedConcurrency);
}
finally {
await faastModule.cleanup();
}
}
(0, ava_1.default)("local provider cleanup stops executions", testCleanup, {});
(0, ava_1.default)("local provider cleanup stops executions with child process", testCleanup, {
childProcess: true
});
const orderConfigs = [
{ childProcess: false, concurrency: 1, maxRetries: 0 },
{ childProcess: true, concurrency: 1, maxRetries: 0 },
{ childProcess: false, concurrency: 2, maxRetries: 0 },
{ childProcess: true, concurrency: 2, maxRetries: 0 },
{ childProcess: false, concurrency: 2, maxRetries: 2 },
{ childProcess: true, concurrency: 2, maxRetries: 2 }
];
for (const config of orderConfigs) {
(0, ava_1.default)(`out of order await (async catch) with ${(0, util_1.inspect)(config)}`, testOrder, config);
}
async function readFirstLogfile(logDirectoryUrl) {
const url = new url_1.URL(logDirectoryUrl);
const buf = await (0, fs_extra_1.readFile)(url.pathname + "/0.log");
return buf
.toString()
.split("\n")
.map(m => m.replace(/^\[(\d+)\]/, "[$pid]"));
}
(0, ava_1.default)("local provider console.log, console.warn, and console.error with child process", async (t) => {
const faastModule = await (0, index_1.faastLocal)(funcs, {
childProcess: true,
concurrency: 1,
gc: "off"
});
try {
await faastModule.functions.consoleLog("Remote console.log output");
await faastModule.functions.consoleWarn("Remote console.warn output");
await faastModule.functions.consoleError("Remote console.error output");
await (0, util_2.sleep)(1000);
await faastModule.cleanup({ deleteResources: false });
const messages = await readFirstLogfile(faastModule.logUrl());
t.truthy(messages.find(s => s === "[$pid]: Remote console.log output"));
t.truthy(messages.find(s => s === "[$pid]: Remote console.warn output"));
t.truthy(messages.find(s => s === "[$pid]: Remote console.error output"));
}
finally {
await faastModule.cleanup({ deleteResources: false });
}
});
(0, ava_1.default)("local provider log files should be appended, not truncated, after child process crash", async (t) => {
const faastModule = await (0, index_1.faastLocal)(funcs, {
childProcess: true,
concurrency: 1,
maxRetries: 1,
gc: "off"
});
try {
await faastModule.functions.consoleLog("output 1");
try {
await faastModule.functions.processExit();
}
catch (err) { }
await faastModule.functions.consoleWarn("output 2");
// Wait for flush
await (0, util_2.sleep)(500);
const messages = await readFirstLogfile(faastModule.logUrl());
t.truthy(messages.find(s => s === "[$pid]: output 1"));
t.truthy(messages.find(s => s === "[$pid]: output 2"));
}
finally {
await faastModule.cleanup({ deleteResources: false });
}
});
(0, ava_1.default)("local provider child process exceptions should result in errors with logUrl", async (t) => {
const faastModule = await (0, index_1.faastLocal)(funcs, {
childProcess: true,
concurrency: 1,
maxRetries: 1,
gc: "off"
});
t.plan(1);
try {
await faastModule.functions.error("synthetic error");
}
catch (err) {
const info = error_1.FaastError.info(err);
t.true(typeof info.logUrl === "string" && info.logUrl.startsWith(" file:///"), (0, util_1.inspect)(err));
}
finally {
await faastModule.cleanup();
}
});
(0, ava_1.default)("local provider child process crashes should result in errors with logUrl", async (t) => {
const faastModule = await (0, index_1.faastLocal)(funcs, {
childProcess: true,
concurrency: 1,
maxRetries: 1,
gc: "off"
});
t.plan(1);
try {
await faastModule.functions.processExit(-1);
}
catch (err) {
const info = error_1.FaastError.info(err);
t.true(typeof info.logUrl === "string" && info.logUrl.startsWith(" file:///"), (0, util_1.inspect)(err));
}
finally {
await faastModule.cleanup();
}
});
(0, ava_1.default)("local provider concurrent executions with child processes", async (t) => {
await testConcurrency(t, {
options: {
childProcess: true
},
maxConcurrency: 5,
expectedConcurrency: 5
});
});
(0, ava_1.default)("local provider no concurrency for cpu bound work without child processes", async (t) => {
await testConcurrency(t, {
options: {
childProcess: false
},
maxConcurrency: 5,
expectedConcurrency: 1
});
});
(0, ava_1.default)("local provider cleanup waits for all child processes to exit", async (t) => {
const faastModule = await (0, index_1.faastLocal)(funcs, {
childProcess: true,
gc: "off"
});
faastModule.functions.spin(5000).catch(_ => { });
while (true) {
await (0, util_2.sleep)(100);
if (faastModule.state.executors.length > 0) {
break;
}
}
t.is(faastModule.state.executors.length, 1, "executor is not running");
await faastModule.cleanup({ gcTimeout: 60 });
t.is(faastModule.state.executors.length, 0, "executors are running after cleanup");
});
(0, ava_1.default)("local unresolved module", async (t) => {
t.plan(1);
try {
await (0, index_1.faastLocal)({});
}
catch (err) {
t.regex(err.message, /Could not find file/);
}
});
(0, ava_1.default)("local issue #37", async (t) => {
// Previously this code caused an exception about module wrapper not being
// re-entrant. The problem was a race condition between wrapper selection
// and execution in local provider. Solved by making wrapper selector a
// regular function instead of an async function.
const m = await (0, index_1.faastLocal)(funcs);
try {
const { identityString: identity } = m.functions;
await identity("a");
const b = identity("b");
const c = identity("c");
await b;
await c;
// Test succeeds if no exceptions are thrown.
t.true(true);
}
finally {
await m.cleanup();
}
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidW5pdC1sb2NhbC50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdGVzdC91bml0LWxvY2FsLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsc0RBQTZDO0FBQzdDLHVDQUFvQztBQUNwQyw2QkFBMEI7QUFDMUIsK0JBQStCO0FBQy9CLG9DQUFvRDtBQUNwRCxvRUFBOEM7QUFDOUMsMENBQTREO0FBQzVELHdDQUEwQztBQUUxQyxLQUFLLFVBQVUsV0FBVyxDQUFDLENBQW1CLEVBQUUsT0FBcUI7SUFDakUsTUFBTSxDQUFDLEdBQUcsTUFBTSxJQUFBLGtCQUFVLEVBQUMsS0FBSyxFQUFFO1FBQzlCLEVBQUUsRUFBRSxLQUFLO1FBQ1QsR0FBRyxPQUFPO0tBQ2IsQ0FBQyxDQUFDO0lBQ0gsSUFBSSxJQUFJLEdBQUcsQ0FBQyxDQUFDO0lBRWIsQ0FBQyxDQUFDLFNBQVM7U0FDTixLQUFLLENBQUMsT0FBTyxDQUFDO1NBQ2QsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFLENBQUM7U0FDakIsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLEdBQUUsQ0FBQyxDQUFDLENBQUM7SUFFcEIsQ0FBQyxDQUFDLFNBQVM7U0FDTixLQUFLLENBQUMsSUFBSSxDQUFDO1NBQ1gsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsSUFBSSxFQUFFLENBQUM7U0FDakIsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLEdBQUUsQ0FBQyxDQUFDLENBQUM7SUFFcEIsTUFBTSxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDbEIsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDbEIsQ0FBQztBQUVELEtBQUssVUFBVSxTQUFTLENBQUMsQ0FBbUIsRUFBRSxPQUFxQjtJQUMvRCxNQUFNLFdBQVcsR0FBRyxNQUFNLElBQUEsa0JBQVUsRUFBQyxLQUFLLEVBQUU7UUFDeEMsRUFB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QUEsa0JBQVUsRUFBQyxFQUFFLENBQUMsQ0FBQztLQUN4QjtJQUFDLE9BQU8sR0FBUSxFQUFFO1FBQ2YsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLHFCQUFxQixDQUFDLENBQUM7S0FDL0M7QUFDTCxDQUFDLENBQUMsQ0FBQztBQUVILElBQUEsYUFBSSxFQUFDLGlCQUFpQixFQUFFLEtBQUssRUFBQyxDQUFDLEVBQUMsRUFBRTtJQUM5QiwwRUFBMEU7SUFDMUUseUVBQXlFO0lBQ3pFLHVFQUF1RTtJQUN2RSxpREFBaUQ7SUFDakQsTUFBTSxDQUFDLEdBQUcsTUFBTSxJQUFBLGtCQUFVLEVBQUMsS0FBSyxDQUFDLENBQUM7SUFDbEMsSUFBSTtRQUNBLE1BQU0sRUFBRSxjQUFjLEVBQUUsUUFBUSxFQUFFLEdBQUcsQ0FBQyxDQUFDLFNBQVMsQ0FBQztRQUNqRCxNQUFNLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNwQixNQUFNLENBQUMsR0FBRyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDeEIsTUFBTSxDQUFDLEdBQUcsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3hCLE1BQU0sQ0FBQyxDQUFDO1FBQ1IsTUFBTSxDQUFDLENBQUM7UUFDUiw2Q0FBNkM7UUFDN0MsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztLQUNoQjtZQUFTO1FBQ04sTUFBTSxDQUFDLENBQUMsT0FBTyxFQUFFLENBQUM7S0FDckI7QUFDTCxDQUFDLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0ZXN0LCB7IEV4ZWN1dGlvbkNvbnRleHQgfSBmcm9tIFwiYXZhXCI7XG5pbXBvcnQgeyByZWFkRmlsZSB9IGZyb20gXCJmcy1leHRyYVwiO1xuaW1wb3J0IHsgVVJMIH0gZnJvbSBcInVybFwiO1xuaW1wb3J0IHsgaW5zcGVjdCB9IGZyb20gXCJ1dGlsXCI7XG5pbXBvcnQgeyBmYWFzdExvY2FsLCBMb2NhbE9wdGlvbnMgfSBmcm9tIFwiLi4vaW5kZXhcIjtcbmltcG9ydCAqIGFzIGZ1bmNzIGZyb20gXCIuL2ZpeHR1cmVzL2Z1bmN0aW9uc1wiO1xuaW1wb3J0IHsgbWVhc3VyZUNvbmN1cnJlbmN5LCBzbGVlcCB9IGZyb20gXCIuL2ZpeHR1cmVzL3V0aWxcIjtcbmltcG9ydCB7IEZhYXN0RXJyb3IgfSBmcm9tIFwiLi4vc3JjL2Vycm9yXCI7XG5cbmFzeW5jIGZ1bmN0aW9uIHRlc3RDbGVhbnVwKHQ6IEV4ZWN1dGlvbkNvbnRleHQsIG9wdGlvbnM6IExvY2FsT3B0aW9ucykge1xuICAgIGNvbnN0IG0gPSBhd2FpdCBmYWFzdExvY2FsKGZ1bmNzLCB7XG4gICAgICAgIGdjOiBcIm9mZlwiLFxuICAgICAgICAuLi5vcHRpb25zXG4gICAgfSk7XG4gICAgbGV0IGRvbmUgPSAwO1xuXG4gICAgbS5mdW5jdGlvbnNcbiAgICAgICAgLmhlbGxvKFwidGhlcmVcIilcbiAgICAgICAgLnRoZW4oXyA9PiBkb25lKyspXG4gICAgICAgIC5jYXRjaChfID0+IHt9KTtcblxuICAgIG0uZnVuY3Rpb25zXG4gICAgICAgIC5zbGVlcCgxMDAwKVxuICAgICAgICAudGhlbihfID0+IGRvbmUrKylcbiAgICAgICAgLmNhdGNoKF8gPT4ge30pO1xuXG4gICAgYXdhaXQgbS5jbGVhbnVwKCk7XG4gICAgdC5pcyhkb25lLCAwKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gdGVzdE9yZGVyKHQ6IEV4ZWN1dGlvbkNvbnRleHQsIG9wdGlvbnM6IExvY2FsT3B0aW9ucykge1xuICAgIGNvbnN0IGZhYXN0TW9kdWxlID0gYXdhaXQgZmFhc3RMb2NhbChmdW5jcywge1xuICAgICAgICBnYzogXCJvZmZcIixcbiAgICAgICAgLi4ub3B0aW9uc1xuICAgIH0pO1xuICAgIHQucGxhbigyKTtcblxuICAgIGNvbnN0IGEgPSBmYWFzdE1vZHVsZS5mdW5jdGlvbnMuZW1wdHlSZWplY3QoKTtcbiAgICBjb25zdCBiID0gZmFhc3RNb2R1bGUuZnVuY3Rpb25zLnNsZWVwKDApO1xuICAgIHQuaXMoYXdhaXQgYiwgdW5kZWZpbmVkKTtcbiAgICB0cnkge1xuICAgICAgICBhd2FpdCBhO1xuICAgIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgICAgIHQuaXMoZXJyLCB1bmRlZmluZWQpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICAgIGF3YWl0IGZhYXN0TW9kdWxlLmNsZWFudXAoKTtcbiAgICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHRlc3RDb25jdXJyZW5jeShcbiAgICB0OiBFeGVjdXRpb25Db250ZXh0LFxuICAgIHtcbiAgICAgICAgb3B0aW9ucyxcbiAgICAgICAgbWF4Q29uY3VycmVuY3ksXG4gICAgICAgIGV4cGVjdGVkQ29uY3VycmVuY3lcbiAgICB9OiB7XG4gICAgICAgIG9wdGlvbnM6IExvY2FsT3B0aW9ucztcbiAgICAgICAgbWF4Q29uY3VycmVuY3k6IG51bWJlcjtcbiAgICAgICAgZXhwZWN0ZWRDb25jdXJyZW5jeTogbnVtYmVyO1xuICAgIH1cbikge1xuICAgIGNvbnN0IGZhYXN0TW9kdWxlID0gYXdhaXQgZmFhc3RMb2NhbChmdW5jcywge1xuICAgICAgICAuLi5vcHRpb25zLFxuICAgICAgICBnYzogXCJvZmZcIixcbiAgICAgICAgY29uY3VycmVuY3k6IG1heENvbmN1cnJlbmN5XG4gICAgfSk7XG5cbiAgICB0cnkge1xuICAgICAgICBjb25zdCBOID0gbWF4Q29uY3VycmVuY3kgKiAyO1xuICAgICAgICBjb25zdCBwcm9taXNlcyA9IFtdO1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IE47IGkrKykge1xuICAgICAgICAgICAgcHJvbWlzZXMucHVzaChmYWFzdE1vZHVsZS5mdW5jdGlvbnMuc3BpbigyMDAwKSk7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCB0aW1pbmdzID0gYXdhaXQgUHJvbWlzZS5hbGwocHJvbWlzZXMpO1xuICAgICAgICB0LmlzKG1lYXN1cmVDb25jdXJyZW5jeSh0aW1pbmdzKSwgZXhwZWN0ZWRDb25jdXJyZW5jeSk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgICAgYXdhaXQgZmFhc3RNb2R1bGUuY2xlYW51cCgpO1xuICAgIH1cbn1cblxudGVzdChcImxvY2FsIHByb3ZpZGVyIGNsZWFudXAgc3RvcHMgZXhlY3V0aW9uc1wiLCB0ZXN0Q2xlYW51cCwge30pO1xudGVzdChcImxvY2FsIHByb3ZpZGVyIGNsZWFudXAgc3RvcHMgZXhlY3V0aW9ucyB3aXRoIGNoaWxkIHByb2Nlc3NcIiwgdGVzdENsZWFudXAsIHtcbiAgICBjaGlsZFByb2Nlc3M6IHRydWVcbn0pO1xuXG5jb25zdCBvcmRlckNvbmZpZ3MgPSBbXG4gICAgeyBjaGlsZFByb2Nlc3M6IGZhbHNlLCBjb25jdXJyZW5jeTogMSwgbWF4UmV0cmllczogMCB9LFxuICAgIHsgY2hpbGRQcm9jZXNzOiB0cnVlLCBjb25jdXJyZW5jeTogMSwgbWF4UmV0cmllczogMCB9LFxuICAgIHsgY2hpbGRQcm9jZXNzOiBmYWxzZSwgY29uY3VycmVuY3k6IDIsIG1heFJldHJpZXM6IDAgfSxcbiAgICB7IGNoaWxkUHJvY2VzczogdHJ1ZSwgY29uY3VycmVuY3k6IDIsIG1heFJldHJpZXM6IDAgfSxcbiAgICB7IGNoaWxkUHJvY2VzczogZmFsc2UsIGNvbmN1cnJlbmN5OiAyLCBtYXhSZXRyaWVzOiAyIH0sXG4gICAgeyBjaGlsZFByb2Nlc3M6IHRydWUsIGNvbmN1cnJlbmN5OiAyLCBtYXhSZXRyaWVzOiAyIH1cbl07XG5cbmZvciAoY29uc3QgY29uZmlnIG9mIG9yZGVyQ29uZmlncykge1xuICAgIHRlc3QoYG91dCBvZiBvcmRlciBhd2FpdCAoYXN5bmMgY2F0Y2gpIHdpdGggJHtpbnNwZWN0KGNvbmZpZyl9YCwgdGVzdE9yZGVyLCBjb25maWcpO1xufVxuXG5hc3luYyBmdW5jdGlvbiByZWFkRmlyc3RMb2dmaWxlKGxvZ0RpcmVjdG9yeVVybDogc3RyaW5nKSB7XG4gICAgY29uc3QgdXJsID0gbmV3IFVSTChsb2dEaXJlY3RvcnlVcmwpO1xuICAgIGNvbnN0IGJ1ZiA9IGF3YWl0IHJlYWRGaWxlKHVybC5wYXRobmFtZSArIFwiLzAubG9nXCIpO1xuICAgIHJldHVybiBidWZcbiAgICAgICAgLnRvU3RyaW5nKClcbiAgICAgICAgLnNwbGl0KFwiXFxuXCIpXG4gICAgICAgIC5tYXAobSA9PiBtLnJlcGxhY2UoL15cXFsoXFxkKylcXF0vLCBcIlskcGlkXVwiKSk7XG59XG5cbnRlc3QoXCJsb2NhbCBwcm92aWRlciBjb25zb2xlLmxvZywgY29uc29sZS53YXJuLCBhbmQgY29uc29sZS5lcnJvciB3aXRoIGNoaWxkIHByb2Nlc3NcIiwgYXN5bmMgdCA9PiB7XG4gICAgY29uc3QgZmFhc3RNb2R1bGUgPSBhd2FpdCBmYWFzdExvY2FsKGZ1bmNzLCB7XG4gICAgICAgIGNoaWxkUHJvY2VzczogdHJ1ZSxcbiAgICAgICAgY29uY3VycmVuY3k6IDEsXG4gICAgICAgIGdjOiBcIm9mZlwiXG4gICAgfSk7XG4gICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgZmFhc3RNb2R1bGUuZnVuY3Rpb25zLmNvbnNvbGVMb2coXCJSZW1vdGUgY29uc29sZS5sb2cgb3V0cHV0XCIpO1xuICAgICAgICBhd2FpdCBmYWFzdE1vZHVsZS5mdW5jdGlvbnMuY29uc29sZVdhcm4oXCJSZW1vdGUgY29uc29sZS53YXJuIG91dHB1dFwiKTtcbiAgICAgICAgYXdhaXQgZmFhc3RNb2R1bGUuZnVuY3Rpb25zLmNvbnNvbGVFcnJvcihcIlJlbW90ZSBjb25zb2xlLmVycm9yIG91dHB1dFwiKTtcbiAgICAgICAgYXdhaXQgc2xlZXAoMTAwMCk7XG4gICAgICAgIGF3YWl0IGZhYXN0TW9kdWxlLmNsZWFudXAoeyBkZWxldGVSZXNvdXJjZXM6IGZhbHNlIH0pO1xuICAgICAgICBjb25zdCBtZXNzYWdlcyA9IGF3YWl0IHJlYWRGaXJzdExvZ2ZpbGUoZmFhc3RNb2R1bGUubG9nVXJsKCkpO1xuICAgICAgICB0LnRydXRoeShtZXNzYWdlcy5maW5kKHMgPT4gcyA9PT0gXCJbJHBpZF06IFJlbW90ZSBjb25zb2xlLmxvZyBvdXRwdXRcIikpO1xuICAgICAgICB0LnRydXRoeShtZXNzYWdlcy5maW5kKHMgPT4gcyA9PT0gXCJbJHBpZF06IFJlbW90ZSBjb25zb2xlLndhcm4gb3V0cHV0XCIpKTtcbiAgICAgICAgdC50cnV0aHkobWVzc2FnZXMuZmluZChzID0+IHMgPT09IFwiWyRwaWRdOiBSZW1vdGUgY29uc29sZS5lcnJvciBvdXRwdXRcIikpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICAgIGF3YWl0IGZhYXN0TW9kdWxlLmNsZWFudXAoeyBkZWxldGVSZXNvdXJjZXM6IGZhbHNlIH0pO1xuICAgIH1cbn0pO1xuXG50ZXN0KFwibG9jYWwgcHJvdmlkZXIgbG9nIGZpbGVzIHNob3VsZCBiZSBhcHBlbmRlZCwgbm90IHRydW5jYXRlZCwgYWZ0ZXIgY2hpbGQgcHJvY2VzcyBjcmFzaFwiLCBhc3luYyB0ID0+IHtcbiAgICBjb25zdCBmYWFzdE1vZHVsZSA9IGF3YWl0IGZhYXN0TG9jYWwoZnVuY3MsIHtcbiAgICAgICAgY2hpbGRQcm9jZXNzOiB0cnVlLFxuICAgICAgICBjb25jdXJyZW5jeTogMSxcbiAgICAgICAgbWF4UmV0cmllczogMSxcbiAgICAgICAgZ2M6IFwib2ZmXCJcbiAgICB9KTtcbiAgICB0cnkge1xuICAgICAgICBhd2FpdCBmYWFzdE1vZHVsZS5mdW5jdGlvbnMuY29uc29sZUxvZyhcIm91dHB1dCAxXCIpO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgZmFhc3RNb2R1bGUuZnVuY3Rpb25zLnByb2Nlc3NFeGl0KCk7XG4gICAgICAgIH0gY2F0Y2ggKGVycjogYW55KSB7fVxuICAgICAgICBhd2FpdCBmYWFzdE1vZHVsZS5mdW5jdGlvbnMuY29uc29sZVdhcm4oXCJvdXRwdXQgMlwiKTtcblxuICAgICAgICAvLyBXYWl0IGZvciBmbHVzaFxuICAgICAgICBhd2FpdCBzbGVlcCg1MDApO1xuICAgICAgICBjb25zdCBtZXNzYWdlcyA9IGF3YWl0IHJlYWRGaXJzdExvZ2ZpbGUoZmFhc3RNb2R1bGUubG9nVXJsKCkpO1xuXG4gICAgICAgIHQudHJ1dGh5KG1lc3NhZ2VzLmZpbmQocyA9PiBzID09PSBcIlskcGlkXTogb3V0cHV0IDFcIikpO1xuICAgICAgICB0LnRydXRoeShtZXNzYWdlcy5maW5kKHMgPT4gcyA9PT0gXCJbJHBpZF06IG91dHB1dCAyXCIpKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgICBhd2FpdCBmYWFzdE1vZHVsZS5jbGVhbnVwKHsgZGVsZXRlUmVzb3VyY2VzOiBmYWxzZSB9KTtcbiAgICB9XG59KTtcblxudGVzdChcImxvY2FsIHByb3ZpZGVyIGNoaWxkIHByb2Nlc3MgZXhjZXB0aW9ucyBzaG91bGQgcmVzdWx0IGluIGVycm9ycyB3aXRoIGxvZ1VybFwiLCBhc3luYyB0ID0+IHtcbiAgICBjb25zdCBmYWFzdE1vZHVsZSA9IGF3YWl0IGZhYXN0TG9jYWwoZnVuY3MsIHtcbiAgICAgICAgY2hpbGRQcm9jZXNzOiB0cnVlLFxuICAgICAgICBjb25jdXJyZW5jeTogMSxcbiAgICAgICAgbWF4UmV0cmllczogMSxcbiAgICAgICAgZ2M6IFwib2ZmXCJcbiAgICB9KTtcbiAgICB0LnBsYW4oMSk7XG4gICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgZmFhc3RNb2R1bGUuZnVuY3Rpb25zLmVycm9yKFwic3ludGhldGljIGVycm9yXCIpO1xuICAgIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgICAgIGNvbnN0IGluZm8gPSBGYWFzdEVycm9yLmluZm8oZXJyKTtcbiAgICAgICAgdC50cnVlKFxuICAgICAgICAgICAgdHlwZW9mIGluZm8ubG9nVXJsID09PSBcInN0cmluZ1wiICYmIGluZm8ubG9nVXJsLnN0YXJ0c1dpdGgoXCIgZmlsZTovLy9cIiksXG4gICAgICAgICAgICBpbnNwZWN0KGVycilcbiAgICAgICAgKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgICBhd2FpdCBmYWFzdE1vZHVsZS5jbGVhbnVwKCk7XG4gICAgfVxufSk7XG5cbnRlc3QoXCJsb2NhbCBwcm92aWRlciBjaGlsZCBwcm9jZXNzIGNyYXNoZXMgc2hvdWxkIHJlc3VsdCBpbiBlcnJvcnMgd2l0aCBsb2dVcmxcIiwgYXN5bmMgdCA9PiB7XG4gICAgY29uc3QgZmFhc3RNb2R1bGUgPSBhd2FpdCBmYWFzdExvY2FsKGZ1bmNzLCB7XG4gICAgICAgIGNoaWxkUHJvY2VzczogdHJ1ZSxcbiAgICAgICAgY29uY3VycmVuY3k6IDEsXG4gICAgICAgIG1heFJldHJpZXM6IDEsXG4gICAgICAgIGdjOiBcIm9mZlwiXG4gICAgfSk7XG4gICAgdC5wbGFuKDEpO1xuICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IGZhYXN0TW9kdWxlLmZ1bmN0aW9ucy5wcm9jZXNzRXhpdCgtMSk7XG4gICAgfSBjYXRjaCAoZXJyOiBhbnkpIHtcbiAgICAgICAgY29uc3QgaW5mbyA9IEZhYXN0RXJyb3IuaW5mbyhlcnIpO1xuICAgICAgICB0LnRydWUoXG4gICAgICAgICAgICB0eXBlb2YgaW5mby5sb2dVcmwgPT09IFwic3RyaW5nXCIgJiYgaW5mby5sb2dVcmwuc3RhcnRzV2l0aChcIiBmaWxlOi8vL1wiKSxcbiAgICAgICAgICAgIGluc3BlY3QoZXJyKVxuICAgICAgICApO1xuICAgIH0gZmluYWxseSB7XG4gICAgICAgIGF3YWl0IGZhYXN0TW9kdWxlLmNsZWFudXAoKTtcbiAgICB9XG59KTtcblxudGVzdChcImxvY2FsIHByb3ZpZGVyIGNvbmN1cnJlbnQgZXhlY3V0aW9ucyB3aXRoIGNoaWxkIHByb2Nlc3Nlc1wiLCBhc3luYyB0ID0+IHtcbiAgICBhd2FpdCB0ZXN0Q29uY3VycmVuY3kodCwge1xuICAgICAgICBvcHRpb25zOiB7XG4gICAgICAgICAgICBjaGlsZFByb2Nlc3M6IHRydWVcbiAgICAgICAgfSxcbiAgICAgICAgbWF4Q29uY3VycmVuY3k6IDUsXG4gICAgICAgIGV4cGVjdGVkQ29uY3VycmVuY3k6IDVcbiAgICB9KTtcbn0pO1xuXG50ZXN0KFwibG9jYWwgcHJvdmlkZXIgbm8gY29uY3VycmVuY3kgZm9yIGNwdSBib3VuZCB3b3JrIHdpdGhvdXQgY2hpbGQgcHJvY2Vzc2VzXCIsIGFzeW5jIHQgPT4ge1xuICAgIGF3YWl0IHRlc3RDb25jdXJyZW5jeSh0LCB7XG4gICAgICAgIG9wdGlvbnM6IHtcbiAgICAgICAgICAgIGNoaWxkUHJvY2VzczogZmFsc2VcbiAgICAgICAgfSxcbiAgICAgICAgbWF4Q29uY3VycmVuY3k6IDUsXG4gICAgICAgIGV4cGVjdGVkQ29uY3VycmVuY3k6IDFcbiAgICB9KTtcbn0pO1xuXG50ZXN0KFwibG9jYWwgcHJvdmlkZXIgY2xlYW51cCB3YWl0cyBmb3IgYWxsIGNoaWxkIHByb2Nlc3NlcyB0byBleGl0XCIsIGFzeW5jIHQgPT4ge1xuICAgIGNvbnN0IGZhYXN0TW9kdWxlID0gYXdhaXQgZmFhc3RMb2NhbChmdW5jcywge1xuICAgICAgICBjaGlsZFByb2Nlc3M6IHRydWUsXG4gICAgICAgIGdjOiBcIm9mZlwiXG4gICAgfSk7XG4gICAgZmFhc3RNb2R1bGUuZnVuY3Rpb25zLnNwaW4oNTAwMCkuY2F0Y2goXyA9PiB7fSk7XG4gICAgd2hpbGUgKHRydWUpIHtcbiAgICAgICAgYXdhaXQgc2xlZXAoMTAwKTtcbiAgICAgICAgaWYgKGZhYXN0TW9kdWxlLnN0YXRlLmV4ZWN1dG9ycy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgIH1cbiAgICB0LmlzKGZhYXN0TW9kdWxlLnN0YXRlLmV4ZWN1dG9ycy5sZW5ndGgsIDEsIFwiZXhlY3V0b3IgaXMgbm90IHJ1bm5pbmdcIik7XG4gICAgYXdhaXQgZmFhc3RNb2R1bGUuY2xlYW51cCh7IGdjVGltZW91dDogNjAgfSk7XG4gICAgdC5pcyhmYWFzdE1vZHVsZS5zdGF0ZS5leGVjdXRvcnMubGVuZ3RoLCAwLCBcImV4ZWN1dG9ycyBhcmUgcnVubmluZyBhZnRlciBjbGVhbnVwXCIpO1xufSk7XG5cbnRlc3QoXCJsb2NhbCB1bnJlc29sdmVkIG1vZHVsZVwiLCBhc3luYyB0ID0+IHtcbiAgICB0LnBsYW4oMSk7XG4gICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgZmFhc3RMb2NhbCh7fSk7XG4gICAgfSBjYXRjaCAoZXJyOiBhbnkpIHtcbiAgICAgICAgdC5yZWdleChlcnIubWVzc2FnZSwgL0NvdWxkIG5vdCBmaW5kIGZpbGUvKTtcbiAgICB9XG59KTtcblxudGVzdChcImxvY2FsIGlzc3VlICMzN1wiLCBhc3luYyB0ID0+IHtcbiAgICAvLyBQcmV2aW91c2x5IHRoaXMgY29kZSBjYXVzZWQgYW4gZXhjZXB0aW9uIGFib3V0IG1vZHVsZSB3cmFwcGVyIG5vdCBiZWluZ1xuICAgIC8vIHJlLWVudHJhbnQuIFRoZSBwcm9ibGVtIHdhcyBhIHJhY2UgY29uZGl0aW9uIGJldHdlZW4gd3JhcHBlciBzZWxlY3Rpb25cbiAgICAvLyBhbmQgZXhlY3V0aW9uIGluIGxvY2FsIHByb3ZpZGVyLiBTb2x2ZWQgYnkgbWFraW5nIHdyYXBwZXIgc2VsZWN0b3IgYVxuICAgIC8vIHJlZ3VsYXIgZnVuY3Rpb24gaW5zdGVhZCBvZiBhbiBhc3luYyBmdW5jdGlvbi5cbiAgICBjb25zdCBtID0gYXdhaXQgZmFhc3RMb2NhbChmdW5jcyk7XG4gICAgdHJ5IHtcbiAgICAgICAgY29uc3QgeyBpZGVudGl0eVN0cmluZzogaWRlbnRpdHkgfSA9IG0uZnVuY3Rpb25zO1xuICAgICAgICBhd2FpdCBpZGVudGl0eShcImFcIik7XG4gICAgICAgIGNvbnN0IGIgPSBpZGVudGl0eShcImJcIik7XG4gICAgICAgIGNvbnN0IGMgPSBpZGVudGl0eShcImNcIik7XG4gICAgICAgIGF3YWl0IGI7XG4gICAgICAgIGF3YWl0IGM7XG4gICAgICAgIC8vIFRlc3Qgc3VjY2VlZHMgaWYgbm8gZXhjZXB0aW9ucyBhcmUgdGhyb3duLlxuICAgICAgICB0LnRydWUodHJ1ZSk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgICAgYXdhaXQgbS5jbGVhbnVwKCk7XG4gICAgfVxufSk7XG4iXX0=