UNPKG

faastjs

Version:

Serverless batch computing made simple.

177 lines 26.1 kB
"use strict"; 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXdzLXF1ZXVlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2F3cy9hd3MtcXVldWUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsZ0VBQTREO0FBSTVELG9DQUF1RDtBQUN2RCxnQ0FBNkI7QUFFN0IsNENBQXNEO0FBQ3RELHNDQUFvQztBQUNwQywwQ0FBc0M7QUFDdEMsd0NBQStFO0FBR3hFLEtBQUssVUFBVSxjQUFjLENBQUMsR0FBUSxFQUFFLElBQVk7SUFDdkQsTUFBTSxLQUFLLEdBQUcsTUFBTSxHQUFHLENBQUMsV0FBVyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUM5QyxPQUFPLEtBQUssQ0FBQyxRQUFTLENBQUM7QUFDM0IsQ0FBQztBQUhELHdDQUdDO0FBRUQsU0FBUyxhQUFhLENBQUMsS0FBYTtJQUNoQyxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxHQUFHLENBQUMsRUFBRSxHQUFHLElBQUksQ0FBQyxDQUFDLENBQUM7QUFDMUMsQ0FBQztBQUVNLEtBQUssVUFBVSx3QkFBd0IsQ0FDMUMsR0FBUSxFQUNSLFFBQWdCLEVBQ2hCLE9BQWdCLEVBQ2hCLEVBQWtCO0lBRWxCLElBQUksQ0FBQztRQUNELE1BQU0sT0FBTyxHQUFHLEVBQUUsUUFBUSxFQUFFLFdBQVcsRUFBRSxJQUFBLHFCQUFTLEVBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztRQUM5RCxNQUFNLEdBQUcsR0FBRyxPQUFPLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQztRQUN2QyxJQUFJLEdBQUcsR0FBRyxNQUFNLEVBQUUsQ0FBQztZQUNmLE1BQU0sSUFBSSxLQUFLLENBQUMsb0JBQW9CLEdBQUcsaUNBQWlDLENBQUMsQ0FBQztRQUM5RSxDQUFDO1FBQ0QsTUFBTSxHQUFHLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ25DLENBQUM7SUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO1FBQ1gsU0FBRyxDQUFD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Q0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLENBQUMsT0FBTyxFQUFFO2dCQUNuRCxXQUFXO2FBQ2QsQ0FBQztZQUNGLFNBQVMsRUFBRSxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUMsT0FBTyxFQUFFO1NBQ25ELENBQUM7SUFDTixDQUFDO1NBQU0sQ0FBQztRQUNKLE1BQU0sT0FBTyxHQUFHLEdBQWMsQ0FBQztRQUMvQixRQUFRLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNuQixLQUFLLFNBQVMsQ0FBQztZQUNmLEtBQUssVUFBVTtnQkFDWCxPQUFPLENBQUMsU0FBUyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsVUFBVyxDQUFDLGFBQWEsQ0FBQyxDQUFDO2dCQUN4RCxNQUFNO1lBQ1YsS0FBSyxZQUFZO2dCQUNiLE1BQU07WUFDVixLQUFLLGlCQUFpQjtnQkFDbEIsTUFBTTtZQUNWLE9BQU8sQ0FBQyxDQUFDLENBQUM7Z0JBQ04sT0FBTyxDQUFDLElBQUksQ0FBQyw4Q0FBOEMsQ0FBQyxDQUFDO1lBQ2pFLENBQUM7UUFDTCxDQUFDO1FBQ0QsT0FBTyxHQUFHLENBQUM7SUFDZixDQUFDO0FBQ0wsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEFib3J0Q29udHJvbGxlciB9IGZyb20gXCJAYXdzLXNkay9hYm9ydC1jb250cm9sbGVyXCI7XG5pbXBvcnQgeyBTTlMgfSBmcm9tIFwiQGF3cy1zZGsvY2xpZW50LXNuc1wiO1xuaW1wb3J0IHsgQ3JlYXRlUXVldWVSZXF1ZXN0LCBTUVMsIE1lc3NhZ2UgYXMgU1FTTWVzc2FnZSB9IGZyb20gXCJAYXdzLXNkay9jbGllbnQtc3FzXCI7XG5pbXBvcnQgeyBTTlNFdmVudCB9IGZyb20gXCJhd3MtbGFtYmRhL3RyaWdnZXIvc25zXCI7XG5pbXBvcnQgeyBGYWFzdEVycm9yLCBGYWFzdEVycm9yTmFtZXMgfSBmcm9tIFwiLi4vZXJyb3JcIjtcbmltcG9ydCB7IGxvZyB9IGZyb20gXCIuLi9sb2dcIjtcbmltcG9ydCB7IE1lc3NhZ2UsIFBvbGxSZXN1bHQgfSBmcm9tIFwiLi4vcHJvdmlkZXJcIjtcbmltcG9ydCB7IGRlc2VyaWFsaXplLCBzZXJpYWxpemUgfSBmcm9tIFwiLi4vc2VyaWFsaXplXCI7XG5pbXBvcnQgeyBkZWZpbmVkIH0gZnJvbSBcIi4uL3NoYXJlZFwiO1xuaW1wb3J0IHsgcmV0cnlPcCB9IGZyb20gXCIuLi90aHJvdHRsZVwiO1xuaW1wb3J0IHsgQ2FsbGluZ0NvbnRleHQsIEZ1bmN0aW9uQ2FsbCwgY3JlYXRlRXJyb3JSZXNwb25zZSB9IGZyb20gXCIuLi93cmFwcGVyXCI7XG5pbXBvcnQgeyBBd3NNZXRyaWNzIH0gZnJvbSBcIi4vYXdzLWZhYXN0XCI7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVTTlNUb3BpYyhzbnM6IFNOUywgTmFtZTogc3RyaW5nKSB7XG4gICAgY29uc3QgdG9waWMgPSBhd2FpdCBzbnMuY3JlYXRlVG9waWMoeyBOYW1lIH0pO1xuICAgIHJldHVybiB0b3BpYy5Ub3BpY0FybiE7XG59XG5cbmZ1bmN0aW9uIGNvdW50UmVxdWVzdHMoYnl0ZXM6IG51bWJlcikge1xuICAgIHJldHVybiBNYXRoLmNlaWwoYnl0ZXMgLyAoNjQgKiAxMDI0KSk7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzZW5kUmVzcG9uc2VRdWV1ZU1lc3NhZ2UoXG4gICAgc3FzOiBTUVMsXG4gICAgUXVldWVVcmw6IHN0cmluZyxcbiAgICBtZXNzYWdlOiBNZXNzYWdlLFxuICAgIGNjOiBDYWxsaW5nQ29udGV4dFxuKSB7XG4gICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcmVxdWVzdCA9IHsgUXVldWVVcmwsIE1lc3NhZ2VCb2R5OiBzZXJpYWxpemUobWVzc2FnZSkgfTtcbiAgICAgICAgY29uc3QgbGVuID0gcmVxdWVzdC5NZXNzYWdlQm9keS5sZW5ndGg7XG4gICAgICAgIGlmIChsZW4gPiAyNjIxNDQpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgUmVzcG9uc2UgbGVuZ3RoICgke2xlbn0gYnl0ZXMpIGV4Y2VlZHMgbGltaXQgb2YgMjU2S2lCYCk7XG4gICAgICAgIH1cbiAgICAgICAgYXdhaXQgc3FzLnNlbmRNZXNzYWdlKHJlcXVlc3QpO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBsb2cud2Fybihgc2VuZFJlc3BvbnNlUXVldWVNZXNzYWdlIGZhaWxlZDogJHtlcnJ9YCk7XG4gICAgICAgIGNvbnN0IGVycm9yUmVzcG9uc2UgPSBjcmVhdGVFcnJvclJlc3BvbnNlKGVyciwgY2MpO1xuICAgICAgICBjb25zdCBlcnJvclJlcXVlc3QgPSB7IFF1ZXVlVXJsLCBNZXNzYWdlQm9keTogc2VyaWFsaXplKGVycm9yUmVzcG9uc2UpIH07XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBhd2FpdCBzcXMuc2VuZE1lc3NhZ2UoZXJyb3JSZXF1ZXN0KTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3JSZXNwb25zZUVycm9yOiBhbnkpIHtcbiAgICAgICAgICAgIGxvZy53YXJuKFxuICAgICAgICAgICAgICAgIGBFcnJvciBzZW5kaW5nIGVycm9yIHJlc3BvbnNlIHRvIHJlc3BvbnNlIHF1ZXVlOiAke2Vycm9yUmVzcG9uc2VFcnJvcn1gXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gcHVibGlzaEZ1bmN0aW9uQ2FsbE1lc3NhZ2UoXG4gICAgc25zOiBTTlMsXG4gICAgVG9waWNBcm46IHN0cmluZyxcbiAgICBtZXNzYWdlOiBGdW5jdGlvbkNhbGwsXG4gICAgbWV0cmljczogQXdzTWV0cmljc1xuKSB7XG4gICAgY29uc3Qgc2VyaWFsaXplZCA9IHNlcmlhbGl6ZShtZXNzYWdlKTtcbiAgICBtZXRyaWNzLnNuczY0a1JlcXVlc3RzICs9IGNvdW50UmVxdWVzdHMoc2VyaWFsaXplZC5sZW5ndGgpO1xuICAgIHJldHVybiByZXRyeU9wKFxuICAgICAgICAoZXJyLCBuKSA9PiBuIDwgNiAmJiBlcnI/Lm1lc3NhZ2U/Lm1hdGNoKC9kb2VzIG5vdCBleGlzdC8pLFxuICAgICAgICAoKSA9PiBzbnMucHVibGlzaCh7IFRvcGljQXJuLCBNZXNzYWdlOiBzZXJpYWxpemVkIH0pXG4gICAgKTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZVNRU1F1ZXVlKFF1ZXVlTmFtZTogc3RyaW5nLCBWVGltZW91dDogbnVtYmVyLCBzcXM6IFNRUykge1xuICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IGNyZWF0ZVF1ZXVlUmVxdWVzdDogQ3JlYXRlUXVldWVSZXF1ZXN0ID0ge1xuICAgICAgICAgICAgUXVldWVOYW1lLFxuICAgICAgICAgICAgQXR0cmlidXRlczoge1xuICAgICAgICAgICAgICAgIFZpc2liaWxpdHlUaW1lb3V0OiBgJHtWVGltZW91dH1gXG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgc3FzLmNyZWF0ZVF1ZXVlKGNyZWF0ZVF1ZXVlUmVxdWVzdCk7XG4gICAgICAgIGNvbnN0IFF1ZXVlVXJsID0gcmVzcG9uc2UuUXVldWVVcmwhO1xuICAgICAgICBjb25zdCBhcm5SZXNwb25zZSA9IGF3YWl0IHNxcy5nZXRRdWV1ZUF0dHJpYnV0ZXMoe1xuICAgICAgICAgICAgUXVldWVVcmwsXG4gICAgICAgICAgICBBdHRyaWJ1dGVOYW1lczogW1wiUXVldWVBcm5cIl1cbiAgICAgICAgfSk7XG4gICAgICAgIGNvbnN0IFF1ZXVlQXJuID0gYXJuUmVzcG9uc2UuQXR0cmlidXRlcz8uUXVldWVBcm47XG4gICAgICAgIHJldHVybiB7IFF1ZXVlVXJsLCBRdWV1ZUFybiB9O1xuICAgIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgICAgIHRocm93IG5ldyBGYWFzdEVycm9yKGVyciwgXCJjcmVhdGUgc3FzIHF1ZXVlXCIpO1xuICAgIH1cbn1cblxuLyogYzggaWdub3JlIG5leHQgICovXG5leHBvcnQgZnVuY3Rpb24gcHJvY2Vzc0F3c0Vycm9yTWVzc2FnZShtZXNzYWdlPzogc3RyaW5nKTogRXJyb3Ige1xuICAgIGxldCBlcnIgPSBuZXcgRmFhc3RFcnJvcihtZXNzYWdlKTtcbiAgICBpZiAoXG4gICAgICAgIG1lc3NhZ2U/Lm1hdGNoKC9Qcm9jZXNzIGV4aXRlZCBiZWZvcmUgY29tcGxldGluZy8pIHx8XG4gICAgICAgIG1lc3NhZ2U/Lm1hdGNoKC9zaWduYWw6IGtpbGxlZC8pIHx8XG4gICAgICAgIG1lc3NhZ2U/Lm1hdGNoKC9SdW50aW1lIGV4aXRlZC8pXG4gICAgKSB7XG4gICAgICAgIGVyciA9IG5ldyBGYWFzdEVycm9yKFxuICAgICAgICAgICAgeyBjYXVzZTogZXJyLCBuYW1lOiBGYWFzdEVycm9yTmFtZXMuRU1FTU9SWSB9LFxuICAgICAgICAgICAgXCJwb3NzaWJseSBvdXQgb2YgbWVtb3J5XCJcbiAgICAgICAgKTtcbiAgICB9IGVsc2UgaWYgKG1lc3NhZ2U/Lm1hdGNoKC90aW1lLykpIHtcbiAgICAgICAgZXJyID0gbmV3IEZhYXN0RXJyb3IoeyBjYXVzZTogZXJyLCBuYW1lOiBGYWFzdEVycm9yTmFtZXMuRVRJTUVPVVQgfSwgXCJ0aW1lb3V0XCIpO1xuICAgIH0gZWxzZSBpZiAobWVzc2FnZT8ubWF0Y2goL0V2ZW50QWdlRXhjZWVkZWQvKSkge1xuICAgICAgICBlcnIgPSBuZXcgRmFhc3RFcnJvcihcbiAgICAgICAgICAgIHsgY2F1c2U6IGVyciwgbmFtZTogRmFhc3RFcnJvck5hbWVzLkVDT05DVVJSRU5DWSB9LFxuICAgICAgICAgICAgXCJjb25jdXJyZW5jeSBsaW1pdCBleGNlZWRlZFwiXG4gICAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiBlcnI7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZWNlaXZlTWVzc2FnZXMoXG4gICAgc3FzOiBTUVMsXG4gICAgUmVzcG9uc2VRdWV1ZVVybDogc3RyaW5nLFxuICAgIG1ldHJpY3M6IEF3c01ldHJpY3MsXG4gICAgY2FuY2VsOiBQcm9taXNlPHZvaWQ+XG4pOiBQcm9taXNlPFBvbGxSZXN1bHQ+IHtcbiAgICB0cnkge1xuICAgICAgICBjb25zdCBNYXhOdW1iZXJPZk1lc3NhZ2VzID0gMTA7XG4gICAgICAgIGNvbnN0IGFib3J0Q29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcbiAgICAgICAgY29uc3QgcmVxdWVzdCA9IHNxcy5yZWNlaXZlTWVzc2FnZShcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBRdWV1ZVVybDogUmVzcG9uc2VRdWV1ZVVybCEsXG4gICAgICAgICAgICAgICAgV2FpdFRpbWVTZWNvbmRzOiAyMCxcbiAgICAgICAgICAgICAgICBNYXhOdW1iZXJPZk1lc3NhZ2VzLFxuICAgICAgICAgICAgICAgIE1lc3NhZ2VBdHRyaWJ1dGVOYW1lczogW1wiQWxsXCJdLFxuICAgICAgICAgICAgICAgIEF0dHJpYnV0ZU5hbWVzOiBbXCJTZW50VGltZXN0YW1wXCJdXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgeyBhYm9ydFNpZ25hbDogYWJvcnRDb250cm9sbGVyLnNpZ25hbCB9XG4gICAgICAgICk7XG4gICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgUHJvbWlzZS5yYWNlKFtyZXF1ZXN0LCBjYW5jZWxdKTtcbiAgICAgICAgaWYgKCFyZXNwb25zZSkge1xuICAgICAgICAgICAgYWJvcnRDb250cm9sbGVyLmFib3J0KCk7XG4gICAgICAgICAgICByZXR1cm4geyBNZXNzYWdlczogW10gfTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHsgTWVzc2FnZXMgPSBbXSB9ID0gcmVzcG9uc2U7XG4gICAgICAgIGNvbnN0IHJlY2VpdmVkQnl0ZXMgPSBNZXNzYWdlcy5yZWR1Y2UoKHN1bSwgbSkgPT4gc3VtICsgKG0uQm9keT8ubGVuZ3RoID8/IDApLCAwKTtcbiAgICAgICAgbWV0cmljcy5vdXRib3VuZEJ5dGVzICs9IHJlY2VpdmVkQnl0ZXM7XG4gICAgICAgIGNvbnN0IGluZmVycmVkU3FzUmVxdWVzdHNSZWNlaXZlZCA9IGNvdW50UmVxdWVzdHMocmVjZWl2ZWRCeXRlcyk7XG4gICAgICAgIC8vIEVhY2ggcmVxdWVzdCBpcyBjb3VudGVkIGFzIGJvdGggc2VudCBhbmQgcmVjZWl2ZWQuXG4gICAgICAgIG1ldHJpY3Muc3FzNjRrUmVxdWVzdHMgKz0gaW5mZXJyZWRTcXNSZXF1ZXN0c1JlY2VpdmVkICogMjtcblxuICAgICAgICBpZiAoTWVzc2FnZXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgc3FzLmRlbGV0ZU1lc3NhZ2VCYXRjaCh7XG4gICAgICAgICAgICAgICAgUXVldWVVcmw6IFJlc3BvbnNlUXVldWVVcmwhLFxuICAgICAgICAgICAgICAgIEVudHJpZXM6IE1lc3NhZ2VzLm1hcChtID0+ICh7XG4gICAgICAgICAgICAgICAgICAgIElkOiBtLk1lc3NhZ2VJZCEsXG4gICAgICAgICAgICAgICAgICAgIFJlY2VpcHRIYW5kbGU6IG0uUmVjZWlwdEhhbmRsZSFcbiAgICAgICAgICAgICAgICB9KSlcbiAgICAgICAgICAgIH0pLmNhdGNoKF8gPT4ge30pO1xuICAgICAgICAgICAgbWV0cmljcy5zcXM2NGtSZXF1ZXN0cysrO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBNZXNzYWdlczogTWVzc2FnZXMubWFwKHByb2Nlc3NJbmNvbWluZ1F1ZXVlTWVzc2FnZSkuZmlsdGVyKGRlZmluZWQpLFxuICAgICAgICAgICAgaXNGdWxsTWVzc2FnZUJhdGNoOiBNZXNzYWdlcy5sZW5ndGggPT09IE1heE51bWJlck9mTWVzc2FnZXNcbiAgICAgICAgfTtcbiAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICB0aHJvdyBuZXcgRmFhc3RFcnJvcihlcnIsIFwicmVjZWl2ZU1lc3NhZ2VzXCIpO1xuICAgIH1cbn1cblxuaW50ZXJmYWNlIExhbWJkYURlc3RpbmF0aW9uRXJyb3Ige1xuICAgIGVycm9yTWVzc2FnZTogc3RyaW5nO1xuICAgIGVycm9yVHlwZT86IHN0cmluZztcbiAgICBzdGFja1RyYWNlPzogc3RyaW5nW107XG59XG5cbmludGVyZmFjZSBMYW1iZGFEZXN0aW5hdGlvbk1lc3NhZ2Uge1xuICAgIHZlcnNpb246IHN0cmluZztcbiAgICB0aW1lc3RhbXA6IHN0cmluZztcbiAgICByZXF1ZXN0Q29udGV4dDoge1xuICAgICAgICByZXF1ZXN0SWQ6IHN0cmluZztcbiAgICAgICAgZnVuY3Rpb25Bcm46IHN0cmluZztcbiAgICAgICAgY29uZGl0aW9uOiBcIlJldHJpZXNFeGhhdXN0ZWRcIiB8IFwiU3VjY2Vzc1wiO1xuICAgICAgICBhcHByb3hpbWF0ZUludm9rZUNvdW50OiBudW1iZXI7XG4gICAgfTtcbiAgICByZXF1ZXN0UGF5bG9hZDogb2JqZWN0O1xuICAgIHJlc3BvbnNlQ29udGV4dDoge1xuICAgICAgICBzdGF0dXNDb2RlOiBudW1iZXI7XG4gICAgICAgIGV4ZWN1dGVkVmVyc2lvbjogc3RyaW5nO1xuICAgICAgICBmdW5jdGlvbkVycm9yPzogc3RyaW5nO1xuICAgIH07XG4gICAgcmVzcG9uc2VQYXlsb2FkOiBMYW1iZGFEZXN0aW5hdGlvbkVycm9yIHwgb2JqZWN0O1xufVxuXG5mdW5jdGlvbiBwcm9jZXNzSW5jb21pbmdRdWV1ZU1lc3NhZ2UobTogU1FTTWVzc2FnZSk6IE1lc3NhZ2UgfCB2b2lkIHtcbiAgICAvLyBBV1MgTGFtYmRhIERlc3RpbmF0aW9uc1xuICAgIC8vIChodHRwczovL2F3cy5hbWF6b24uY29tL2Jsb2dzL2NvbXB1dGUvaW50cm9kdWNpbmctYXdzLWxhbWJkYS1kZXN0aW5hdGlvbnMvKVxuICAgIC8vIGFyZSB1c2VkIHRvIHJvdXRlIGZhaWx1cmVzIHRvIHRoZSByZXNwb25zZSBxdWV1ZS4gVGhlc2VcbiAgICAvLyBtZXNzYWdlcyBhcmUgZ2VuZXJhdGVkIGJ5IEFXUyBMYW1iZGEgYW5kIGFyZSBjb25zdHJhaW5lZCB0byB0aGUgZm9ybWF0IGl0XG4gICAgLy8gcHJvdmlkZXMuXG4gICAgY29uc3QgcmF3ID0gZGVzZXJpYWxpemUobS5Cb2R5ISk7XG4gICAgaWYgKHJhdy5yZXNwb25zZUNvbnRleHQpIHtcbiAgICAgICAgY29uc3QgbWVzc2FnZSA9IHJhdyBhcyBMYW1iZGFEZXN0aW5hdGlvbk1lc3NhZ2U7XG4gICAgICAgIGNvbnN0IHNuc01lc3NhZ2UgPSBtZXNzYWdlLnJlcXVlc3RQYXlsb2FkIGFzIFNOU0V2ZW50O1xuICAgICAgICBjb25zdCByZWNvcmQgPSBzbnNNZXNzYWdlLlJlY29yZHNbMF07XG4gICAgICAgIGNvbnN0IHNDYWxsOiBGdW5jdGlvbkNhbGwgPSBkZXNlcmlhbGl6ZShyZWNvcmQuU25zLk1lc3NhZ2UpO1xuICAgICAgICBsZXQgZXJyb3I6IEVycm9yIHwgdW5kZWZpbmVkO1xuICAgICAgICBjb25zdCBkZXN0aW5hdGlvbkVycm9yID0gbWVzc2FnZS5yZXNwb25zZVBheWxvYWQgYXMgTGFtYmRhRGVzdGluYXRpb25FcnJvcjtcbiAgICAgICAgaWYgKGRlc3RpbmF0aW9uRXJyb3IpIHtcbiAgICAgICAgICAgIGVycm9yID0gcHJvY2Vzc0F3c0Vycm9yTWVzc2FnZShkZXN0aW5hdGlvbkVycm9yLmVycm9yTWVzc2FnZSk7XG4gICAgICAgICAgICBlcnJvci5zdGFjayA9IGRlc3RpbmF0aW9uRXJyb3Iuc3RhY2tUcmFjZT8uam9pbihcIlxcblwiKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGVycm9yID0gcHJvY2Vzc0F3c0Vycm9yTWVzc2FnZShtZXNzYWdlLnJlcXVlc3RDb250ZXh0LmNvbmRpdGlvbik7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgZXhlY3V0aW9uSWQgPSBtZXNzYWdlLnJlcXVlc3RDb250ZXh0LnJlcXVlc3RJZDtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIC4uLmNyZWF0ZUVycm9yUmVzcG9uc2UoZXJyb3IsIHtcbiAgICAgICAgICAgICAgICBjYWxsOiBzQ2FsbCxcbiAgICAgICAgICAgICAgICBzdGFydFRpbWU6IG5ldyBEYXRlKHJlY29yZC5TbnMuVGltZXN0YW1wKS5nZXRUaW1lKCksXG4gICAgICAgICAgICAgICAgZXhlY3V0aW9uSWRcbiAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZShtZXNzYWdlLnRpbWVzdGFtcCkuZ2V0VGltZSgpXG4gICAgICAgIH07XG4gICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3QgbWVzc2FnZSA9IHJhdyBhcyBNZXNzYWdlO1xuICAgICAgICBzd2l0Y2ggKG1lc3NhZ2Uua2luZCkge1xuICAgICAgICAgICAgY2FzZSBcInByb21pc2VcIjpcbiAgICAgICAgICAgIGNhc2UgXCJpdGVyYXRvclwiOlxuICAgICAgICAgICAgICAgIG1lc3NhZ2UudGltZXN0YW1wID0gTnVtYmVyKG0uQXR0cmlidXRlcyEuU2VudFRpbWVzdGFtcCk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICBjYXNlIFwiY3B1bWV0cmljc1wiOlxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgY2FzZSBcImZ1bmN0aW9uc3RhcnRlZFwiOlxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgZGVmYXVsdDoge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUud2FybihgVW5rbm93biBtZXNzYWdlIHJlY2VpdmVkIGZyb20gcmVzcG9uc2UgcXVldWVgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmF3O1xuICAgIH1cbn1cbiJdfQ==