faastjs
Version:
Serverless batch computing made simple.
177 lines • 25.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.receiveMessages = exports.processAwsErrorMessage = exports.createSQSQueue = exports.publishFunctionCallMessage = exports.sendResponseQueueMessage = exports.createSNSTopic = void 0;
const abort_controller_1 = require("@aws-sdk/abort-controller");
const error_1 = require("../error");
const log_1 = require("../log");
const serialize_1 = require("../serialize");
const shared_1 = require("../shared");
const throttle_1 = require("../throttle");
const wrapper_1 = require("../wrapper");
async function createSNSTopic(sns, Name) {
const topic = await sns.createTopic({ Name });
return topic.TopicArn;
}
exports.createSNSTopic = createSNSTopic;
function countRequests(bytes) {
return Math.ceil(bytes / (64 * 1024));
}
async function sendResponseQueueMessage(sqs, QueueUrl, message, cc) {
try {
const request = { QueueUrl, MessageBody: (0, serialize_1.serialize)(message) };
const len = request.MessageBody.length;
if (len > 262144) {
throw new Error(`Response length (${len} bytes) exceeds limit of 256KiB`);
}
await sqs.sendMessage(request);
}
catch (err) {
log_1.log.warn(`sendResponseQueueMessage failed: ${err}`);
const errorResponse = (0, wrapper_1.createErrorResponse)(err, cc);
const errorRequest = { QueueUrl, MessageBody: (0, serialize_1.serialize)(errorResponse) };
try {
await sqs.sendMessage(errorRequest);
}
catch (errorResponseError) {
log_1.log.warn(`Error sending error response to response queue: ${errorResponseError}`);
}
}
}
exports.sendResponseQueueMessage = sendResponseQueueMessage;
function publishFunctionCallMessage(sns, TopicArn, message, metrics) {
const serialized = (0, serialize_1.serialize)(message);
metrics.sns64kRequests += countRequests(serialized.length);
return (0, throttle_1.retryOp)((err, n) => n < 6 && err?.message?.match(/does not exist/), () => sns.publish({ TopicArn, Message: serialized }));
}
exports.publishFunctionCallMessage = publishFunctionCallMessage;
async function createSQSQueue(QueueName, VTimeout, sqs) {
try {
const createQueueRequest = {
QueueName,
Attributes: {
VisibilityTimeout: `${VTimeout}`
}
};
const response = await sqs.createQueue(createQueueRequest);
const QueueUrl = response.QueueUrl;
const arnResponse = await sqs.getQueueAttributes({
QueueUrl,
AttributeNames: ["QueueArn"]
});
const QueueArn = arnResponse.Attributes?.QueueArn;
return { QueueUrl, QueueArn };
}
catch (err) {
throw new error_1.FaastError(err, "create sqs queue");
}
}
exports.createSQSQueue = createSQSQueue;
/* c8 ignore next */
function processAwsErrorMessage(message) {
let err = new error_1.FaastError(message);
if (message?.match(/Process exited before completing/) ||
message?.match(/signal: killed/) ||
message?.match(/Runtime exited/)) {
err = new error_1.FaastError({ cause: err, name: error_1.FaastErrorNames.EMEMORY }, "possibly out of memory");
}
else if (message?.match(/time/)) {
err = new error_1.FaastError({ cause: err, name: error_1.FaastErrorNames.ETIMEOUT }, "timeout");
}
else if (message?.match(/EventAgeExceeded/)) {
err = new error_1.FaastError({ cause: err, name: error_1.FaastErrorNames.ECONCURRENCY }, "concurrency limit exceeded");
}
return err;
}
exports.processAwsErrorMessage = processAwsErrorMessage;
async function receiveMessages(sqs, ResponseQueueUrl, metrics, cancel) {
try {
const MaxNumberOfMessages = 10;
const abortController = new abort_controller_1.AbortController();
const request = sqs.receiveMessage({
QueueUrl: ResponseQueueUrl,
WaitTimeSeconds: 20,
MaxNumberOfMessages,
MessageAttributeNames: ["All"],
AttributeNames: ["SentTimestamp"]
}, { abortSignal: abortController.signal });
const response = await Promise.race([request, cancel]);
if (!response) {
abortController.abort();
return { Messages: [] };
}
const { Messages = [] } = response;
const receivedBytes = Messages.reduce((sum, m) => sum + (m.Body?.length ?? 0), 0);
metrics.outboundBytes += receivedBytes;
const inferredSqsRequestsReceived = countRequests(receivedBytes);
// Each request is counted as both sent and received.
metrics.sqs64kRequests += inferredSqsRequestsReceived * 2;
if (Messages.length > 0) {
sqs.deleteMessageBatch({
QueueUrl: ResponseQueueUrl,
Entries: Messages.map(m => ({
Id: m.MessageId,
ReceiptHandle: m.ReceiptHandle
}))
}).catch(_ => { });
metrics.sqs64kRequests++;
}
return {
Messages: Messages.map(processIncomingQueueMessage).filter(shared_1.defined),
isFullMessageBatch: Messages.length === MaxNumberOfMessages
};
}
catch (err) {
throw new error_1.FaastError(err, "receiveMessages");
}
}
exports.receiveMessages = receiveMessages;
function processIncomingQueueMessage(m) {
// AWS Lambda Destinations
// (https://aws.amazon.com/blogs/compute/introducing-aws-lambda-destinations/)
// are used to route failures to the response queue. These
// messages are generated by AWS Lambda and are constrained to the format it
// provides.
const raw = (0, serialize_1.deserialize)(m.Body);
if (raw.responseContext) {
const message = raw;
const snsMessage = message.requestPayload;
const record = snsMessage.Records[0];
const sCall = (0, serialize_1.deserialize)(record.Sns.Message);
let error;
const destinationError = message.responsePayload;
if (destinationError) {
error = processAwsErrorMessage(destinationError.errorMessage);
error.stack = destinationError.stackTrace?.join("\n");
}
else {
error = processAwsErrorMessage(message.requestContext.condition);
}
const executionId = message.requestContext.requestId;
return {
...(0, wrapper_1.createErrorResponse)(error, {
call: sCall,
startTime: new Date(record.Sns.Timestamp).getTime(),
executionId
}),
timestamp: new Date(message.timestamp).getTime()
};
}
else {
const message = raw;
switch (message.kind) {
case "promise":
case "iterator":
message.timestamp = Number(m.Attributes.SentTimestamp);
break;
case "cpumetrics":
break;
case "functionstarted":
break;
default: {
console.warn(`Unknown message received from response queue`);
}
}
return raw;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXdzLXF1ZXVlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2F3cy9hd3MtcXVldWUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsZ0VBQTREO0FBSTVELG9DQUF1RDtBQUN2RCxnQ0FBNkI7QUFFN0IsNENBQXNEO0FBQ3RELHNDQUFvQztBQUNwQywwQ0FBc0M7QUFDdEMsd0NBQStFO0FBR3hFLEtBQUssVUFBVSxjQUFjLENBQUMsR0FBUSxFQUFFLElBQVk7SUFDdkQsTUFBTSxLQUFLLEdBQUcsTUFBTSxHQUFHLENBQUMsV0FBVyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUM5QyxPQUFPLEtBQUssQ0FBQyxRQUFTLENBQUM7QUFDM0IsQ0FBQztBQUhELHdDQUdDO0FBRUQsU0FBUyxhQUFhLENBQUMsS0FBYTtJQUNoQyxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxHQUFHLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxDQUFDLENBQUM7QUFDMUMsQ0FBQztBQUVNLEtBQUssVUFBVSx3QkFBd0IsQ0FDMUMsR0FBUSxFQUNSLFFBQWdCLEVBQ2hCLE9BQWdCLEVBQ2hCLEVBQWtCO0lBRWxCLElBQUk7UUFDQSxNQUFNLE9BQU8sR0FBRyxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsSUFBQSxxQkFBUyxFQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7UUFDOUQsTUFBTSxHQUFHLEdBQUcsT0FBTyxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUM7UUFDdkMsSUFBSSxHQUFHLEdBQUcsTUFBTSxFQUFFO1lBQ2QsTUFBTSxJQUFJLEtBQUssQ0FBQyxvQkFBb0IsR0FBRyxpQ0FBaUMsQ0FBQyxDQUFDO1NBQzdFO1FBQ0QsTUFBTSxHQUFHLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0tBQ2xDO0lBQUMsT0FBTyxHQUFHLEVBQUU7UUFDVixTQUFHLENBQUMsSUFBSSxDQUFDLG9DQUFvQyxHQUFHLEVB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LEdBQWMsQ0FBQztRQUMvQixRQUFRLE9BQU8sQ0FBQyxJQUFJLEVBQUU7WUFDbEIsS0FBSyxTQUFTLENBQUM7WUFDZixLQUFLLFVBQVU7Z0JBQ1gsT0FBTyxDQUFDLFNBQVMsR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLFVBQVcsQ0FBQyxhQUFhLENBQUMsQ0FBQztnQkFDeEQsTUFBTTtZQUNWLEtBQUssWUFBWTtnQkFDYixNQUFNO1lBQ1YsS0FBSyxpQkFBaUI7Z0JBQ2xCLE1BQU07WUFDVixPQUFPLENBQUMsQ0FBQztnQkFDTCxPQUFPLENBQUMsSUFBSSxDQUFDLDhDQUE4QyxDQUFDLENBQUM7YUFDaEU7U0FDSjtRQUNELE9BQU8sR0FBRyxDQUFDO0tBQ2Q7QUFDTCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQWJvcnRDb250cm9sbGVyIH0gZnJvbSBcIkBhd3Mtc2RrL2Fib3J0LWNvbnRyb2xsZXJcIjtcbmltcG9ydCB7IFNOUyB9IGZyb20gXCJAYXdzLXNkay9jbGllbnQtc25zXCI7XG5pbXBvcnQgeyBDcmVhdGVRdWV1ZVJlcXVlc3QsIFNRUywgTWVzc2FnZSBhcyBTUVNNZXNzYWdlIH0gZnJvbSBcIkBhd3Mtc2RrL2NsaWVudC1zcXNcIjtcbmltcG9ydCB7IFNOU0V2ZW50IH0gZnJvbSBcImF3cy1sYW1iZGEvdHJpZ2dlci9zbnNcIjtcbmltcG9ydCB7IEZhYXN0RXJyb3IsIEZhYXN0RXJyb3JOYW1lcyB9IGZyb20gXCIuLi9lcnJvclwiO1xuaW1wb3J0IHsgbG9nIH0gZnJvbSBcIi4uL2xvZ1wiO1xuaW1wb3J0IHsgTWVzc2FnZSwgUG9sbFJlc3VsdCB9IGZyb20gXCIuLi9wcm92aWRlclwiO1xuaW1wb3J0IHsgZGVzZXJpYWxpemUsIHNlcmlhbGl6ZSB9IGZyb20gXCIuLi9zZXJpYWxpemVcIjtcbmltcG9ydCB7IGRlZmluZWQgfSBmcm9tIFwiLi4vc2hhcmVkXCI7XG5pbXBvcnQgeyByZXRyeU9wIH0gZnJvbSBcIi4uL3Rocm90dGxlXCI7XG5pbXBvcnQgeyBDYWxsaW5nQ29udGV4dCwgRnVuY3Rpb25DYWxsLCBjcmVhdGVFcnJvclJlc3BvbnNlIH0gZnJvbSBcIi4uL3dyYXBwZXJcIjtcbmltcG9ydCB7IEF3c01ldHJpY3MgfSBmcm9tIFwiLi9hd3MtZmFhc3RcIjtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZVNOU1RvcGljKHNuczogU05TLCBOYW1lOiBzdHJpbmcpIHtcbiAgICBjb25zdCB0b3BpYyA9IGF3YWl0IHNucy5jcmVhdGVUb3BpYyh7IE5hbWUgfSk7XG4gICAgcmV0dXJuIHRvcGljLlRvcGljQXJuITtcbn1cblxuZnVuY3Rpb24gY291bnRSZXF1ZXN0cyhieXRlczogbnVtYmVyKSB7XG4gICAgcmV0dXJuIE1hdGguY2VpbChieXRlcyAvICg2NCAqIDEwMjQpKTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNlbmRSZXNwb25zZVF1ZXVlTWVzc2FnZShcbiAgICBzcXM6IFNRUyxcbiAgICBRdWV1ZVVybDogc3RyaW5nLFxuICAgIG1lc3NhZ2U6IE1lc3NhZ2UsXG4gICAgY2M6IENhbGxpbmdDb250ZXh0XG4pIHtcbiAgICB0cnkge1xuICAgICAgICBjb25zdCByZXF1ZXN0ID0geyBRdWV1ZVVybCwgTWVzc2FnZUJvZHk6IHNlcmlhbGl6ZShtZXNzYWdlKSB9O1xuICAgICAgICBjb25zdCBsZW4gPSByZXF1ZXN0Lk1lc3NhZ2VCb2R5Lmxlbmd0aDtcbiAgICAgICAgaWYgKGxlbiA+IDI2MjE0NCkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBSZXNwb25zZSBsZW5ndGggKCR7bGVufSBieXRlcykgZXhjZWVkcyBsaW1pdCBvZiAyNTZLaUJgKTtcbiAgICAgICAgfVxuICAgICAgICBhd2FpdCBzcXMuc2VuZE1lc3NhZ2UocmVxdWVzdCk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGxvZy53YXJuKGBzZW5kUmVzcG9uc2VRdWV1ZU1lc3NhZ2UgZmFpbGVkOiAke2Vycn1gKTtcbiAgICAgICAgY29uc3QgZXJyb3JSZXNwb25zZSA9IGNyZWF0ZUVycm9yUmVzcG9uc2UoZXJyLCBjYyk7XG4gICAgICAgIGNvbnN0IGVycm9yUmVxdWVzdCA9IHsgUXVldWVVcmwsIE1lc3NhZ2VCb2R5OiBzZXJpYWxpemUoZXJyb3JSZXNwb25zZSkgfTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGF3YWl0IHNxcy5zZW5kTWVzc2FnZShlcnJvclJlcXVlc3QpO1xuICAgICAgICB9IGNhdGNoIChlcnJvclJlc3BvbnNlRXJyb3I6IGFueSkge1xuICAgICAgICAgICAgbG9nLndhcm4oXG4gICAgICAgICAgICAgICAgYEVycm9yIHNlbmRpbmcgZXJyb3IgcmVzcG9uc2UgdG8gcmVzcG9uc2UgcXVldWU6ICR7ZXJyb3JSZXNwb25zZUVycm9yfWBcbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBwdWJsaXNoRnVuY3Rpb25DYWxsTWVzc2FnZShcbiAgICBzbnM6IFNOUyxcbiAgICBUb3BpY0Fybjogc3RyaW5nLFxuICAgIG1lc3NhZ2U6IEZ1bmN0aW9uQ2FsbCxcbiAgICBtZXRyaWNzOiBBd3NNZXRyaWNzXG4pIHtcbiAgICBjb25zdCBzZXJpYWxpemVkID0gc2VyaWFsaXplKG1lc3NhZ2UpO1xuICAgIG1ldHJpY3Muc25zNjRrUmVxdWVzdHMgKz0gY291bnRSZXF1ZXN0cyhzZXJpYWxpemVkLmxlbmd0aCk7XG4gICAgcmV0dXJuIHJldHJ5T3AoXG4gICAgICAgIChlcnIsIG4pID0+IG4gPCA2ICYmIGVycj8ubWVzc2FnZT8ubWF0Y2goL2RvZXMgbm90IGV4aXN0LyksXG4gICAgICAgICgpID0+IHNucy5wdWJsaXNoKHsgVG9waWNBcm4sIE1lc3NhZ2U6IHNlcmlhbGl6ZWQgfSlcbiAgICApO1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlU1FTUXVldWUoUXVldWVOYW1lOiBzdHJpbmcsIFZUaW1lb3V0OiBudW1iZXIsIHNxczogU1FTKSB7XG4gICAgdHJ5IHtcbiAgICAgICAgY29uc3QgY3JlYXRlUXVldWVSZXF1ZXN0OiBDcmVhdGVRdWV1ZVJlcXVlc3QgPSB7XG4gICAgICAgICAgICBRdWV1ZU5hbWUsXG4gICAgICAgICAgICBBdHRyaWJ1dGVzOiB7XG4gICAgICAgICAgICAgICAgVmlzaWJpbGl0eVRpbWVvdXQ6IGAke1ZUaW1lb3V0fWBcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBzcXMuY3JlYXRlUXVldWUoY3JlYXRlUXVldWVSZXF1ZXN0KTtcbiAgICAgICAgY29uc3QgUXVldWVVcmwgPSByZXNwb25zZS5RdWV1ZVVybCE7XG4gICAgICAgIGNvbnN0IGFyblJlc3BvbnNlID0gYXdhaXQgc3FzLmdldFF1ZXVlQXR0cmlidXRlcyh7XG4gICAgICAgICAgICBRdWV1ZVVybCxcbiAgICAgICAgICAgIEF0dHJpYnV0ZU5hbWVzOiBbXCJRdWV1ZUFyblwiXVxuICAgICAgICB9KTtcbiAgICAgICAgY29uc3QgUXVldWVBcm4gPSBhcm5SZXNwb25zZS5BdHRyaWJ1dGVzPy5RdWV1ZUFybjtcbiAgICAgICAgcmV0dXJuIHsgUXVldWVVcmwsIFF1ZXVlQXJuIH07XG4gICAgfSBjYXRjaCAoZXJyOiBhbnkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEZhYXN0RXJyb3IoZXJyLCBcImNyZWF0ZSBzcXMgcXVldWVcIik7XG4gICAgfVxufVxuXG4vKiBjOCBpZ25vcmUgbmV4dCAgKi9cbmV4cG9ydCBmdW5jdGlvbiBwcm9jZXNzQXdzRXJyb3JNZXNzYWdlKG1lc3NhZ2U/OiBzdHJpbmcpOiBFcnJvciB7XG4gICAgbGV0IGVyciA9IG5ldyBGYWFzdEVycm9yKG1lc3NhZ2UpO1xuICAgIGlmIChcbiAgICAgICAgbWVzc2FnZT8ubWF0Y2goL1Byb2Nlc3MgZXhpdGVkIGJlZm9yZSBjb21wbGV0aW5nLykgfHxcbiAgICAgICAgbWVzc2FnZT8ubWF0Y2goL3NpZ25hbDoga2lsbGVkLykgfHxcbiAgICAgICAgbWVzc2FnZT8ubWF0Y2goL1J1bnRpbWUgZXhpdGVkLylcbiAgICApIHtcbiAgICAgICAgZXJyID0gbmV3IEZhYXN0RXJyb3IoXG4gICAgICAgICAgICB7IGNhdXNlOiBlcnIsIG5hbWU6IEZhYXN0RXJyb3JOYW1lcy5FTUVNT1JZIH0sXG4gICAgICAgICAgICBcInBvc3NpYmx5IG91dCBvZiBtZW1vcnlcIlxuICAgICAgICApO1xuICAgIH0gZWxzZSBpZiAobWVzc2FnZT8ubWF0Y2goL3RpbWUvKSkge1xuICAgICAgICBlcnIgPSBuZXcgRmFhc3RFcnJvcih7IGNhdXNlOiBlcnIsIG5hbWU6IEZhYXN0RXJyb3JOYW1lcy5FVElNRU9VVCB9LCBcInRpbWVvdXRcIik7XG4gICAgfSBlbHNlIGlmIChtZXNzYWdlPy5tYXRjaCgvRXZlbnRBZ2VFeGNlZWRlZC8pKSB7XG4gICAgICAgIGVyciA9IG5ldyBGYWFzdEVycm9yKFxuICAgICAgICAgICAgeyBjYXVzZTogZXJyLCBuYW1lOiBGYWFzdEVycm9yTmFtZXMuRUNPTkNVUlJFTkNZIH0sXG4gICAgICAgICAgICBcImNvbmN1cnJlbmN5IGxpbWl0IGV4Y2VlZGVkXCJcbiAgICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIGVycjtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlY2VpdmVNZXNzYWdlcyhcbiAgICBzcXM6IFNRUyxcbiAgICBSZXNwb25zZVF1ZXVlVXJsOiBzdHJpbmcsXG4gICAgbWV0cmljczogQXdzTWV0cmljcyxcbiAgICBjYW5jZWw6IFByb21pc2U8dm9pZD5cbik6IFByb21pc2U8UG9sbFJlc3VsdD4ge1xuICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IE1heE51bWJlck9mTWVzc2FnZXMgPSAxMDtcbiAgICAgICAgY29uc3QgYWJvcnRDb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpO1xuICAgICAgICBjb25zdCByZXF1ZXN0ID0gc3FzLnJlY2VpdmVNZXNzYWdlKFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIFF1ZXVlVXJsOiBSZXNwb25zZVF1ZXVlVXJsISxcbiAgICAgICAgICAgICAgICBXYWl0VGltZVNlY29uZHM6IDIwLFxuICAgICAgICAgICAgICAgIE1heE51bWJlck9mTWVzc2FnZXMsXG4gICAgICAgICAgICAgICAgTWVzc2FnZUF0dHJpYnV0ZU5hbWVzOiBbXCJBbGxcIl0sXG4gICAgICAgICAgICAgICAgQXR0cmlidXRlTmFtZXM6IFtcIlNlbnRUaW1lc3RhbXBcIl1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB7IGFib3J0U2lnbmFsOiBhYm9ydENvbnRyb2xsZXIuc2lnbmFsIH1cbiAgICAgICAgKTtcbiAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBQcm9taXNlLnJhY2UoW3JlcXVlc3QsIGNhbmNlbF0pO1xuICAgICAgICBpZiAoIXJlc3BvbnNlKSB7XG4gICAgICAgICAgICBhYm9ydENvbnRyb2xsZXIuYWJvcnQoKTtcbiAgICAgICAgICAgIHJldHVybiB7IE1lc3NhZ2VzOiBbXSB9O1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgeyBNZXNzYWdlcyA9IFtdIH0gPSByZXNwb25zZTtcbiAgICAgICAgY29uc3QgcmVjZWl2ZWRCeXRlcyA9IE1lc3NhZ2VzLnJlZHVjZSgoc3VtLCBtKSA9PiBzdW0gKyAobS5Cb2R5Py5sZW5ndGggPz8gMCksIDApO1xuICAgICAgICBtZXRyaWNzLm91dGJvdW5kQnl0ZXMgKz0gcmVjZWl2ZWRCeXRlcztcbiAgICAgICAgY29uc3QgaW5mZXJyZWRTcXNSZXF1ZXN0c1JlY2VpdmVkID0gY291bnRSZXF1ZXN0cyhyZWNlaXZlZEJ5dGVzKTtcbiAgICAgICAgLy8gRWFjaCByZXF1ZXN0IGlzIGNvdW50ZWQgYXMgYm90aCBzZW50IGFuZCByZWNlaXZlZC5cbiAgICAgICAgbWV0cmljcy5zcXM2NGtSZXF1ZXN0cyArPSBpbmZlcnJlZFNxc1JlcXVlc3RzUmVjZWl2ZWQgKiAyO1xuXG4gICAgICAgIGlmIChNZXNzYWdlcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBzcXMuZGVsZXRlTWVzc2FnZUJhdGNoKHtcbiAgICAgICAgICAgICAgICBRdWV1ZVVybDogUmVzcG9uc2VRdWV1ZVVybCEsXG4gICAgICAgICAgICAgICAgRW50cmllczogTWVzc2FnZXMubWFwKG0gPT4gKHtcbiAgICAgICAgICAgICAgICAgICAgSWQ6IG0uTWVzc2FnZUlkISxcbiAgICAgICAgICAgICAgICAgICAgUmVjZWlwdEhhbmRsZTogbS5SZWNlaXB0SGFuZGxlIVxuICAgICAgICAgICAgICAgIH0pKVxuICAgICAgICAgICAgfSkuY2F0Y2goXyA9PiB7fSk7XG4gICAgICAgICAgICBtZXRyaWNzLnNxczY0a1JlcXVlc3RzKys7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIE1lc3NhZ2VzOiBNZXNzYWdlcy5tYXAocHJvY2Vzc0luY29taW5nUXVldWVNZXNzYWdlKS5maWx0ZXIoZGVmaW5lZCksXG4gICAgICAgICAgICBpc0Z1bGxNZXNzYWdlQmF0Y2g6IE1lc3NhZ2VzLmxlbmd0aCA9PT0gTWF4TnVtYmVyT2ZNZXNzYWdlc1xuICAgICAgICB9O1xuICAgIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgICAgIHRocm93IG5ldyBGYWFzdEVycm9yKGVyciwgXCJyZWNlaXZlTWVzc2FnZXNcIik7XG4gICAgfVxufVxuXG5pbnRlcmZhY2UgTGFtYmRhRGVzdGluYXRpb25FcnJvciB7XG4gICAgZXJyb3JNZXNzYWdlOiBzdHJpbmc7XG4gICAgZXJyb3JUeXBlPzogc3RyaW5nO1xuICAgIHN0YWNrVHJhY2U/OiBzdHJpbmdbXTtcbn1cblxuaW50ZXJmYWNlIExhbWJkYURlc3RpbmF0aW9uTWVzc2FnZSB7XG4gICAgdmVyc2lvbjogc3RyaW5nO1xuICAgIHRpbWVzdGFtcDogc3RyaW5nO1xuICAgIHJlcXVlc3RDb250ZXh0OiB7XG4gICAgICAgIHJlcXVlc3RJZDogc3RyaW5nO1xuICAgICAgICBmdW5jdGlvbkFybjogc3RyaW5nO1xuICAgICAgICBjb25kaXRpb246IFwiUmV0cmllc0V4aGF1c3RlZFwiIHwgXCJTdWNjZXNzXCI7XG4gICAgICAgIGFwcHJveGltYXRlSW52b2tlQ291bnQ6IG51bWJlcjtcbiAgICB9O1xuICAgIHJlcXVlc3RQYXlsb2FkOiBvYmplY3Q7XG4gICAgcmVzcG9uc2VDb250ZXh0OiB7XG4gICAgICAgIHN0YXR1c0NvZGU6IG51bWJlcjtcbiAgICAgICAgZXhlY3V0ZWRWZXJzaW9uOiBzdHJpbmc7XG4gICAgICAgIGZ1bmN0aW9uRXJyb3I/OiBzdHJpbmc7XG4gICAgfTtcbiAgICByZXNwb25zZVBheWxvYWQ6IExhbWJkYURlc3RpbmF0aW9uRXJyb3IgfCBvYmplY3Q7XG59XG5cbmZ1bmN0aW9uIHByb2Nlc3NJbmNvbWluZ1F1ZXVlTWVzc2FnZShtOiBTUVNNZXNzYWdlKTogTWVzc2FnZSB8IHZvaWQge1xuICAgIC8vIEFXUyBMYW1iZGEgRGVzdGluYXRpb25zXG4gICAgLy8gKGh0dHBzOi8vYXdzLmFtYXpvbi5jb20vYmxvZ3MvY29tcHV0ZS9pbnRyb2R1Y2luZy1hd3MtbGFtYmRhLWRlc3RpbmF0aW9ucy8pXG4gICAgLy8gYXJlIHVzZWQgdG8gcm91dGUgZmFpbHVyZXMgdG8gdGhlIHJlc3BvbnNlIHF1ZXVlLiBUaGVzZVxuICAgIC8vIG1lc3NhZ2VzIGFyZSBnZW5lcmF0ZWQgYnkgQVdTIExhbWJkYSBhbmQgYXJlIGNvbnN0cmFpbmVkIHRvIHRoZSBmb3JtYXQgaXRcbiAgICAvLyBwcm92aWRlcy5cbiAgICBjb25zdCByYXcgPSBkZXNlcmlhbGl6ZShtLkJvZHkhKTtcbiAgICBpZiAocmF3LnJlc3BvbnNlQ29udGV4dCkge1xuICAgICAgICBjb25zdCBtZXNzYWdlID0gcmF3IGFzIExhbWJkYURlc3RpbmF0aW9uTWVzc2FnZTtcbiAgICAgICAgY29uc3Qgc25zTWVzc2FnZSA9IG1lc3NhZ2UucmVxdWVzdFBheWxvYWQgYXMgU05TRXZlbnQ7XG4gICAgICAgIGNvbnN0IHJlY29yZCA9IHNuc01lc3NhZ2UuUmVjb3Jkc1swXTtcbiAgICAgICAgY29uc3Qgc0NhbGw6IEZ1bmN0aW9uQ2FsbCA9IGRlc2VyaWFsaXplKHJlY29yZC5TbnMuTWVzc2FnZSk7XG4gICAgICAgIGxldCBlcnJvcjogRXJyb3IgfCB1bmRlZmluZWQ7XG4gICAgICAgIGNvbnN0IGRlc3RpbmF0aW9uRXJyb3IgPSBtZXNzYWdlLnJlc3BvbnNlUGF5bG9hZCBhcyBMYW1iZGFEZXN0aW5hdGlvbkVycm9yO1xuICAgICAgICBpZiAoZGVzdGluYXRpb25FcnJvcikge1xuICAgICAgICAgICAgZXJyb3IgPSBwcm9jZXNzQXdzRXJyb3JNZXNzYWdlKGRlc3RpbmF0aW9uRXJyb3IuZXJyb3JNZXNzYWdlKTtcbiAgICAgICAgICAgIGVycm9yLnN0YWNrID0gZGVzdGluYXRpb25FcnJvci5zdGFja1RyYWNlPy5qb2luKFwiXFxuXCIpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZXJyb3IgPSBwcm9jZXNzQXdzRXJyb3JNZXNzYWdlKG1lc3NhZ2UucmVxdWVzdENvbnRleHQuY29uZGl0aW9uKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBleGVjdXRpb25JZCA9IG1lc3NhZ2UucmVxdWVzdENvbnRleHQucmVxdWVzdElkO1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgLi4uY3JlYXRlRXJyb3JSZXNwb25zZShlcnJvciwge1xuICAgICAgICAgICAgICAgIGNhbGw6IHNDYWxsLFxuICAgICAgICAgICAgICAgIHN0YXJ0VGltZTogbmV3IERhdGUocmVjb3JkLlNucy5UaW1lc3RhbXApLmdldFRpbWUoKSxcbiAgICAgICAgICAgICAgICBleGVjdXRpb25JZFxuICAgICAgICAgICAgfSksXG4gICAgICAgICAgICB0aW1lc3RhbXA6IG5ldyBEYXRlKG1lc3NhZ2UudGltZXN0YW1wKS5nZXRUaW1lKClcbiAgICAgICAgfTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCBtZXNzYWdlID0gcmF3IGFzIE1lc3NhZ2U7XG4gICAgICAgIHN3aXRjaCAobWVzc2FnZS5raW5kKSB7XG4gICAgICAgICAgICBjYXNlIFwicHJvbWlzZVwiOlxuICAgICAgICAgICAgY2FzZSBcIml0ZXJhdG9yXCI6XG4gICAgICAgICAgICAgICAgbWVzc2FnZS50aW1lc3RhbXAgPSBOdW1iZXIobS5BdHRyaWJ1dGVzIS5TZW50VGltZXN0YW1wKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGNhc2UgXCJjcHVtZXRyaWNzXCI6XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlIFwiZnVuY3Rpb25zdGFydGVkXCI6XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBkZWZhdWx0OiB7XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKGBVbmtub3duIG1lc3NhZ2UgcmVjZWl2ZWQgZnJvbSByZXNwb25zZSBxdWV1ZWApO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiByYXc7XG4gICAgfVxufVxuIl19