UNPKG

turbo-remote-cache-construct

Version:

A Turborepo Remote Cache implementation using AWS API Gateway, Lambda, S3, and DynamoDB.

258 lines 37.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.APIGateway = void 0; const apigateway = require("aws-cdk-lib/aws-apigateway"); const logs = require("aws-cdk-lib/aws-logs"); const cdk = require("aws-cdk-lib"); const constructs_1 = require("constructs"); const get_artifact_1 = require("./get-artifact"); const head_artifact_1 = require("./head-artifact"); const put_artifact_1 = require("./put-artifact"); class APIGateway extends constructs_1.Construct { constructor(scope, id, props) { super(scope, id); const logGroup = logs.LogGroup.fromLogGroupName(this, 'LogGroup', '/aws/apigateway/turbo-remote-cache-api'); let tokenAuthorizer; if (props.lambdaFunctions.authorizerFunction) { tokenAuthorizer = new apigateway.TokenAuthorizer(this, 'TokenAuthorizer', { handler: props.lambdaFunctions.authorizerFunction, resultsCacheTtl: cdk.Duration.minutes(60), identitySource: 'method.request.header.Authorization', }); } const api = new apigateway.RestApi(this, 'TurboRemoteCacheApi', { restApiName: 'Turborepo Remote Cache API', description: 'Turborepo is an intelligent build system optimized for JavaScript and TypeScript codebases.', cloudWatchRole: true, deployOptions: { documentationVersion: '8.0.0', loggingLevel: apigateway.MethodLoggingLevel.INFO, accessLogDestination: new apigateway.LogGroupLogDestination(logGroup), accessLogFormat: apigateway.AccessLogFormat.jsonWithStandardFields(), dataTraceEnabled: true, tracingEnabled: true, }, binaryMediaTypes: ['application/octet-stream'], ...(tokenAuthorizer ? { defaultMethodOptions: { authorizationType: apigateway.AuthorizationType.CUSTOM, authorizer: tokenAuthorizer } } : {}), ...props.apiProps, }); new apigateway.CfnDocumentationVersion(this, 'DocumentationVersion', { documentationVersion: '8.0.0', restApiId: api.restApiId, }); const v8Resource = api.root.addResource('v8'); const artifactsResource = v8Resource.addResource('artifacts'); const eventsResource = artifactsResource.addResource('events'); eventsResource.addMethod('POST', new apigateway.LambdaIntegration(props.lambdaFunctions.recordEventsFunction), { operationName: 'recordEvents', methodResponses: [{ statusCode: '200' }], requestParameters: { 'method.request.header.x-artifact-client-ci': false, 'method.request.header.x-artifact-client-interactive': false, }, requestModels: { 'application/json': new apigateway.Model(this, 'RecordEventsModel', { restApi: api, contentType: 'application/json', modelName: 'RecordEventsModel', schema: { type: apigateway.JsonSchemaType.ARRAY, items: { type: apigateway.JsonSchemaType.OBJECT, properties: { sessionId: { type: apigateway.JsonSchemaType.STRING }, source: { type: apigateway.JsonSchemaType.STRING, enum: ['LOCAL', 'REMOTE'] }, event: { type: apigateway.JsonSchemaType.STRING, enum: ['HIT', 'MISS'] }, hash: { type: apigateway.JsonSchemaType.STRING }, duration: { type: apigateway.JsonSchemaType.NUMBER }, }, required: ['sessionId', 'source', 'hash', 'event'], }, }, }), }, }); const recordEventDocumentationPart = { description: 'Records an artifacts cache usage event. The body of this request is an array of cache usage events. The supported event types are `HIT` and `MISS`. The source is either `LOCAL` the cache event was on the users filesystem cache or `REMOTE` if the cache event is for a remote cache. When the event is a `HIT` the request also accepts a number `duration` which is the time taken to generate the artifact in the cache.', summary: 'Record an artifacts cache usage event', tags: ['artifacts'], }; new apigateway.CfnDocumentationPart(this, 'ArtifactsEventsDocumentationPart', { location: { type: 'METHOD', method: 'POST', path: '/v8/artifacts/events', }, properties: JSON.stringify(recordEventDocumentationPart), restApiId: api.restApiId, }); const statusResource = artifactsResource.addResource('status'); statusResource.addMethod('GET', new apigateway.LambdaIntegration(props.lambdaFunctions.statusFunction), { operationName: 'status', methodResponses: [ { statusCode: '200', responseModels: { 'application/json': new apigateway.Model(this, 'StatusResponseModel', { restApi: api, contentType: 'application/json', modelName: 'StatusResponseModel', schema: { type: apigateway.JsonSchemaType.OBJECT, properties: { status: { type: apigateway.JsonSchemaType.STRING, enum: ['disabled', 'enabled', 'over_limit', 'paused'] }, }, required: ['status'], }, }), }, }, ], }); const statusDocumentationPart = { description: 'Check the status of Remote Caching for this principal. Returns a JSON-encoded status indicating if Remote Caching is enabled, disabled, or disabled due to usage limits.', summary: 'Get status of Remote Caching for this principal', tags: ['artifacts'], }; new apigateway.CfnDocumentationPart(this, 'ArtifactsStatusDocumentationPart', { location: { type: 'METHOD', method: 'GET', path: '/v8/artifacts/status', }, properties: JSON.stringify(statusDocumentationPart), restApiId: api.restApiId, }); const hashResource = artifactsResource.addResource('{hash}'); hashResource.addMethod('OPTIONS', new apigateway.LambdaIntegration(props.lambdaFunctions.preflightArtifactFunction), { operationName: 'preflightArtifact', methodResponses: [{ statusCode: '200' }], }); // GET /v8/artifacts/{hash} (0, get_artifact_1.getArtifactIntegration)(this, { artifactsBucket: props.artifactsBucket, s3Credentials: props.s3Credentials, api, hashResource, }); // HEAD /v8/artifacts/{hash} // Check that a cache artifact with the given `hash` exists. This request returns response headers only // and is equivalent to a `GET` request to this endpoint where the response contains no body. (0, head_artifact_1.headArtifactIntegration)(this, { artifactsBucket: props.artifactsBucket, s3Credentials: props.s3Credentials, api, hashResource, }); // PUT /v8/artifacts/{hash} // Uploads a cache artifact identified by the `hash` specified on the path. // The cache artifact can then be downloaded with the provided `hash`. (0, put_artifact_1.putArtifactIntegration)(this, { artifactsBucket: props.artifactsBucket, s3Credentials: props.s3Credentials, api, hashResource, }); // POST /v8/artifacts // Query information about an array of artifacts. artifactsResource.addMethod('POST', new apigateway.LambdaIntegration(props.lambdaFunctions.artifactQueryFunction), { operationName: 'artifactQuery', methodResponses: [{ statusCode: '200' }], requestModels: { 'application/json': new apigateway.Model(this, 'ArtifactQueryModel', { restApi: api, contentType: 'application/json', modelName: 'ArtifactQueryModel', schema: { type: apigateway.JsonSchemaType.OBJECT, properties: { hashes: { type: apigateway.JsonSchemaType.ARRAY, items: { type: apigateway.JsonSchemaType.STRING } }, }, required: ['hashes'], }, }), }, }); const artifactQueryDocumentationPart = { description: 'Query information about an array of artifacts.', summary: 'Query information about an artifact', tags: ['artifacts'], }; new apigateway.CfnDocumentationPart(this, 'ArtifactsQueryDocumentationPart', { location: { type: 'METHOD', method: 'POST', path: '/v8/artifacts', }, properties: JSON.stringify(artifactQueryDocumentationPart), restApiId: api.restApiId, }); // turbo login // TODO: implement turbo login for third party JWTs // const turborepoResource = api.root.addResource('turborepo'); // const tokenResource = turborepoResource.addResource('token'); // // GET /v8/turborepo/token // tokenResource.addMethod('GET', new apigateway.LambdaIntegration(props.lambdaFunctions.initiateLoginFunction), { // operationName: 'initiateLogin', // }); // const initiateLoginDocumentationPart = { // description: 'Initiates a login process for Turborepo.', // summary: 'Initiate login', // tags: ['login'], // } // new apigateway.CfnDocumentationPart(this, 'TurborepoInitiateLoginDocumentationPart', { // location: { // type: 'METHOD', // method: 'GET', // path: '/v8/turborepo/token', // }, // properties: JSON.stringify(initiateLoginDocumentationPart), // restApiId: api.restApiId, // }); // // GET /v8/turborepo/success // const successResource = turborepoResource.addResource('success'); // successResource.addMethod('GET', new apigateway.LambdaIntegration(props.lambdaFunctions.loginSuccessFunction), { // operationName: 'loginSuccess', // }); // const loginSuccessDocumentationPart = { // description: 'Handles the success of a login process for Turborepo.', // summary: 'Login success', // tags: ['login'], // } // new apigateway.CfnDocumentationPart(this, 'TurborepoLoginSuccessDocumentationPart', { // location: { // type: 'METHOD', // method: 'GET', // path: '/v8/turborepo/success', // }, // properties: JSON.stringify(loginSuccessDocumentationPart), // restApiId: api.restApiId, // }); const v2Resource = api.root.addResource('v2'); const userResource = v2Resource.addResource('user'); // GET /v2/user userResource.addMethod('GET', new apigateway.LambdaIntegration(props.lambdaFunctions.getUserInfoFunction), { operationName: 'getUserInfo', }); const getUserInfoDocumentationPart = { description: 'Retrieves information about the authenticated user.', summary: 'Get user info', tags: ['login'], }; new apigateway.CfnDocumentationPart(this, 'TurborepoUserInfoDocumentationPart', { location: { type: 'METHOD', method: 'GET', path: '/v2/user', }, properties: JSON.stringify(getUserInfoDocumentationPart), restApiId: api.restApiId, }); // cloudfront domain name for CNAME new cdk.CfnOutput(this, 'CloudfrontAliasDomainName', { value: api.domainName?.domainNameAliasDomainName ?? 'N/A', }); } } exports.APIGateway = APIGateway; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2FwaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSx5REFBeUQ7QUFHekQsNkNBQTZDO0FBQzdDLG1DQUFtQztBQUNuQywyQ0FBdUM7QUFFdkMsaURBQXdEO0FBQ3hELG1EQUEwRDtBQUMxRCxpREFBd0Q7QUFTeEQsTUFBYSxVQUFXLFNBQVEsc0JBQVM7SUFDdkMsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUFzQjtRQUM5RCxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQ2pCLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxFQUFFLFVBQVUsRUFBRSx3Q0FBd0MsQ0FBQyxDQUFDO1FBRTVHLElBQUksZUFBdUQsQ0FBQztRQUM1RCxJQUFJLEtBQUssQ0FBQyxlQUFlLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztZQUM3QyxlQUFlLEdBQUcsSUFBSSxVQUFVLENBQUMsZUFBZSxDQUFDLElBQUksRUFBRSxpQkFBaUIsRUFBRTtnQkFDeEUsT0FBTyxFQUFFLEtBQUssQ0FBQyxlQUFlLENBQUMsa0JBQWtCO2dCQUNqRCxlQUFlLEVBQUUsR0FBRyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO2dCQUN6QyxjQUFjLEVBQUUscUNBQXFDO2FBQ3RELENBQUMsQ0FBQztRQUNMLENBQUM7UUFFRCxNQUFNLEdBQUcsR0FBRyxJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLHFCQUFxQixFQUFFO1lBQzlELFdBQVcsRUFBRSw0QkFBNEI7WUFDekMsV0FBVyxFQUFFLDZGQUE2RjtZQUMxRyxjQUFjLEVBQUUsSUFBSTtZQUNwQixhQUFhLEVBQUU7Z0JBQ2Isb0JBQW9CLEVBQUUsT0FBTztnQkFDN0IsWUFBWSxFQUFFLFVBQVUsQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJO2dCQUNoRCxvQkFBb0Is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QyxLQUFLLEVBQUUsSUFBSSxVQUFVLENBQUMsaUJBQWlCLENBQUMsS0FBSyxDQUFDLGVBQWUsQ0FBQyxtQkFBbUIsQ0FBQyxFQUFFO1lBQ3pHLGFBQWEsRUFBRSxhQUFhO1NBQzdCLENBQUMsQ0FBQztRQUVILE1BQU0sNEJBQTRCLEdBQUc7WUFDbkMsV0FBVyxFQUFFLHFEQUFxRDtZQUNsRSxPQUFPLEVBQUUsZUFBZTtZQUN4QixJQUFJLEVBQUUsQ0FBQyxPQUFPLENBQUM7U0FDaEIsQ0FBQTtRQUVELElBQUksVUFBVSxDQUFDLG9CQUFvQixDQUFDLElBQUksRUFBRSxvQ0FBb0MsRUFBRTtZQUM5RSxRQUFRLEVBQUU7Z0JBQ1IsSUFBSSxFQUFFLFFBQVE7Z0JBQ2QsTUFBTSxFQUFFLEtBQUs7Z0JBQ2IsSUFBSSxFQUFFLFVBQVU7YUFDakI7WUFDRCxVQUFVLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyw0QkFBNEIsQ0FBQztZQUN4RCxTQUFTLEVBQUUsR0FBRyxDQUFDLFNBQVM7U0FDekIsQ0FBQyxDQUFDO1FBRUgsbUNBQW1DO1FBQ25DLElBQUksR0FBRyxDQUFDLFNBQVMsQ0FBQyxJQUFJLEVBQUUsMkJBQTJCLEVBQUU7WUFDbkQsS0FBSyxFQUFFLEdBQUcsQ0FBQyxVQUFVLEVBQUUseUJBQXlCLElBQUksS0FBSztTQUMxRCxDQUFDLENBQUM7SUFDTCxDQUFDO0NBQ0Y7QUFwUkQsZ0NBb1JDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgYXBpZ2F0ZXdheSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtYXBpZ2F0ZXdheSc7XG5pbXBvcnQgKiBhcyBzMyBmcm9tICdhd3MtY2RrLWxpYi9hd3MtczMnO1xuaW1wb3J0ICogYXMgaWFtIGZyb20gJ2F3cy1jZGstbGliL2F3cy1pYW0nO1xuaW1wb3J0ICogYXMgbG9ncyBmcm9tICdhd3MtY2RrLWxpYi9hd3MtbG9ncyc7XG5pbXBvcnQgKiBhcyBjZGsgZnJvbSAnYXdzLWNkay1saWInO1xuaW1wb3J0IHsgQ29uc3RydWN0IH0gZnJvbSBcImNvbnN0cnVjdHNcIjtcbmltcG9ydCB7IExhbWJkYUZ1bmN0aW9ucyB9IGZyb20gJy4vbGFtYmRhJztcbmltcG9ydCB7IGdldEFydGlmYWN0SW50ZWdyYXRpb24gfSBmcm9tICcuL2dldC1hcnRpZmFjdCc7XG5pbXBvcnQgeyBoZWFkQXJ0aWZhY3RJbnRlZ3JhdGlvbiB9IGZyb20gJy4vaGVhZC1hcnRpZmFjdCc7XG5pbXBvcnQgeyBwdXRBcnRpZmFjdEludGVncmF0aW9uIH0gZnJvbSAnLi9wdXQtYXJ0aWZhY3QnO1xuXG5pbnRlcmZhY2UgQVBJR2F0ZXdheVByb3BzIHtcbiAgYXBpUHJvcHM/OiBhcGlnYXRld2F5LlJlc3RBcGlQcm9wcztcbiAgbGFtYmRhRnVuY3Rpb25zOiBMYW1iZGFGdW5jdGlvbnM7XG4gIGFydGlmYWN0c0J1Y2tldDogczMuQnVja2V0O1xuICBzM0NyZWRlbnRpYWxzOiBpYW0uUm9sZTtcbn1cblxuZXhwb3J0IGNsYXNzIEFQSUdhdGV3YXkgZXh0ZW5kcyBDb25zdHJ1Y3Qge1xuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogQVBJR2F0ZXdheVByb3BzKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkKTtcbiAgICBjb25zdCBsb2dHcm91cCA9IGxvZ3MuTG9nR3JvdXAuZnJvbUxvZ0dyb3VwTmFtZSh0aGlzLCAnTG9nR3JvdXAnLCAnL2F3cy9hcGlnYXRld2F5L3R1cmJvLXJlbW90ZS1jYWNoZS1hcGknKTtcblxuICAgIGxldCB0b2tlbkF1dGhvcml6ZXI6IGFwaWdhdGV3YXkuVG9rZW5BdXRob3JpemVyIHwgdW5kZWZpbmVkO1xuICAgIGlmIChwcm9wcy5sYW1iZGFGdW5jdGlvbnMuYXV0aG9yaXplckZ1bmN0aW9uKSB7XG4gICAgICB0b2tlbkF1dGhvcml6ZXIgPSBuZXcgYXBpZ2F0ZXdheS5Ub2tlbkF1dGhvcml6ZXIodGhpcywgJ1Rva2VuQXV0aG9yaXplcicsIHtcbiAgICAgICAgaGFuZGxlcjogcHJvcHMubGFtYmRhRnVuY3Rpb25zLmF1dGhvcml6ZXJGdW5jdGlvbixcbiAgICAgICAgcmVzdWx0c0NhY2hlVHRsOiBjZGsuRHVyYXRpb24ubWludXRlcyg2MCksXG4gICAgICAgIGlkZW50aXR5U291cmNlOiAnbWV0aG9kLnJlcXVlc3QuaGVhZGVyLkF1dGhvcml6YXRpb24nLFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgY29uc3QgYXBpID0gbmV3IGFwaWdhdGV3YXkuUmVzdEFwaSh0aGlzLCAnVHVyYm9SZW1vdGVDYWNoZUFwaScsIHtcbiAgICAgIHJlc3RBcGlOYW1lOiAnVHVyYm9yZXBvIFJlbW90ZSBDYWNoZSBBUEknLFxuICAgICAgZGVzY3JpcHRpb246ICdUdXJib3JlcG8gaXMgYW4gaW50ZWxsaWdlbnQgYnVpbGQgc3lzdGVtIG9wdGltaXplZCBmb3IgSmF2YVNjcmlwdCBhbmQgVHlwZVNjcmlwdCBjb2RlYmFzZXMuJyxcbiAgICAgIGNsb3VkV2F0Y2hSb2xlOiB0cnVlLFxuICAgICAgZGVwbG95T3B0aW9uczoge1xuICAgICAgICBkb2N1bWVudGF0aW9uVmVyc2lvbjogJzguMC4wJyxcbiAgICAgICAgbG9nZ2luZ0xldmVsOiBhcGlnYXRld2F5Lk1ldGhvZExvZ2dpbmdMZXZlbC5JTkZPLFxuICAgICAgICBhY2Nlc3NMb2dEZXN0aW5hdGlvbjogbmV3IGFwaWdhdGV3YXkuTG9nR3JvdXBMb2dEZXN0aW5hdGlvbihsb2dHcm91cCksXG4gICAgICAgIGFjY2Vzc0xvZ0Zvcm1hdDogYXBpZ2F0ZXdheS5BY2Nlc3NMb2dGb3JtYXQuanNvbldpdGhTdGFuZGFyZEZpZWxkcygpLFxuICAgICAgICBkYXRhVHJhY2VFbmFibGVkOiB0cnVlLFxuICAgICAgICB0cmFjaW5nRW5hYmxlZDogdHJ1ZSxcbiAgICAgIH0sXG4gICAgICBiaW5hcnlNZWRpYVR5cGVzOiBbJ2FwcGxpY2F0aW9uL29jdGV0LXN0cmVhbSddLFxuICAgICAgLi4uKHRva2VuQXV0aG9yaXplciA/IHsgZGVmYXVsdE1ldGhvZE9wdGlvbnM6IHsgYXV0aG9yaXphdGlvblR5cGU6IGFwaWdhdGV3YXkuQXV0aG9yaXphdGlvblR5cGUuQ1VTVE9NLCBhdXRob3JpemVyOiB0b2tlbkF1dGhvcml6ZXIgfSB9IDoge30pLFxuICAgICAgLi4ucHJvcHMuYXBpUHJvcHMsXG4gICAgfSk7XG5cbiAgICBuZXcgYXBpZ2F0ZXdheS5DZm5Eb2N1bWVudGF0aW9uVmVyc2lvbih0aGlzLCAnRG9jdW1lbnRhdGlvblZlcnNpb24nLCB7XG4gICAgICBkb2N1bWVudGF0aW9uVmVyc2lvbjogJzguMC4wJyxcbiAgICAgIHJlc3RBcGlJZDogYXBpLnJlc3RBcGlJZCxcbiAgICB9KTtcblxuICAgIGNvbnN0IHY4UmVzb3VyY2UgPSBhcGkucm9vdC5hZGRSZXNvdXJjZSgndjgnKTtcbiAgICBjb25zdCBhcnRpZmFjdHNSZXNvdXJjZSA9IHY4UmVzb3VyY2UuYWRkUmVzb3VyY2UoJ2FydGlmYWN0cycpO1xuXG4gICAgY29uc3QgZXZlbnRzUmVzb3VyY2UgPSBhcnRpZmFjdHNSZXNvdXJjZS5hZGRSZXNvdXJjZSgnZXZlbnRzJyk7XG4gICAgZXZlbnRzUmVzb3VyY2UuYWRkTWV0aG9kKCdQT1NUJywgbmV3IGFwaWdhdGV3YXkuTGFtYmRhSW50ZWdyYXRpb24ocHJvcHMubGFtYmRhRnVuY3Rpb25zLnJlY29yZEV2ZW50c0Z1bmN0aW9uKSwge1xuICAgICAgb3BlcmF0aW9uTmFtZTogJ3JlY29yZEV2ZW50cycsXG4gICAgICBtZXRob2RSZXNwb25zZXM6IFt7IHN0YXR1c0NvZGU6ICcyMDAnIH1dLFxuICAgICAgcmVxdWVzdFBhcmFtZXRlcnM6IHtcbiAgICAgICAgJ21ldGhvZC5yZXF1ZXN0LmhlYWRlci54LWFydGlmYWN0LWNsaWVudC1jaSc6IGZhbHNlLFxuICAgICAgICAnbWV0aG9kLnJlcXVlc3QuaGVhZGVyLngtYXJ0aWZhY3QtY2xpZW50LWludGVyYWN0aXZlJzogZmFsc2UsXG4gICAgICB9LFxuICAgICAgcmVxdWVzdE1vZGVsczoge1xuICAgICAgICAnYXBwbGljYXRpb24vanNvbic6IG5ldyBhcGlnYXRld2F5Lk1vZGVsKHRoaXMsICdSZWNvcmRFdmVudHNNb2RlbCcsIHtcbiAgICAgICAgICByZXN0QXBpOiBhcGksXG4gICAgICAgICAgY29udGVudFR5cGU6ICdhcHBsaWNhdGlvbi9qc29uJyxcbiAgICAgICAgICBtb2RlbE5hbWU6ICdSZWNvcmRFdmVudHNNb2RlbCcsXG4gICAgICAgICAgc2NoZW1hOiB7XG4gICAgICAgICAgICB0eXBlOiBhcGlnYXRld2F5Lkpzb25TY2hlbWFUeXBlLkFSUkFZLFxuICAgICAgICAgICAgaXRlbXM6IHtcbiAgICAgICAgICAgICAgdHlwZTogYXBpZ2F0ZXdheS5Kc29uU2NoZW1hVHlwZS5PQkpFQ1QsXG4gICAgICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgICAgICBzZXNzaW9uSWQ6IHsgdHlwZTogYXBpZ2F0ZXdheS5Kc29uU2NoZW1hVHlwZS5TVFJJTkcgfSxcbiAgICAgICAgICAgICAgICBzb3VyY2U6IHsgdHlwZTogYXBpZ2F0ZXdheS5Kc29uU2NoZW1hVHlwZS5TVFJJTkcsIGVudW06IFsnTE9DQUwnLCAnUkVNT1RFJ10gfSxcbiAgICAgICAgICAgICAgICBldmVudDogeyB0eXBlOiBhcGlnYXRld2F5Lkpzb25TY2hlbWFUeXBlLlNUUklORywgZW51bTogWydISVQnLCAnTUlTUyddIH0sXG4gICAgICAgICAgICAgICAgaGFzaDogeyB0eXBlOiBhcGlnYXRld2F5Lkpzb25TY2hlbWFUeXBlLlNUUklORyB9LFxuICAgICAgICAgICAgICAgIGR1cmF0aW9uOiB7IHR5cGU6IGFwaWdhdGV3YXkuSnNvblNjaGVtYVR5cGUuTlVNQkVSIH0sXG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIHJlcXVpcmVkOiBbJ3Nlc3Npb25JZCcsICdzb3VyY2UnLCAnaGFzaCcsICdldmVudCddLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9LFxuICAgICAgICB9KSxcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBjb25zdCByZWNvcmRFdmVudERvY3VtZW50YXRpb25QYXJ0ID0ge1xuICAgICAgZGVzY3JpcHRpb246ICdSZWNvcmRzIGFuIGFydGlmYWN0cyBjYWNoZSB1c2FnZSBldmVudC4gVGhlIGJvZHkgb2YgdGhpcyByZXF1ZXN0IGlzIGFuIGFycmF5IG9mIGNhY2hlIHVzYWdlIGV2ZW50cy4gVGhlIHN1cHBvcnRlZCBldmVudCB0eXBlcyBhcmUgYEhJVGAgYW5kIGBNSVNTYC4gVGhlIHNvdXJjZSBpcyBlaXRoZXIgYExPQ0FMYCB0aGUgY2FjaGUgZXZlbnQgd2FzIG9uIHRoZSB1c2VycyBmaWxlc3lzdGVtIGNhY2hlIG9yIGBSRU1PVEVgIGlmIHRoZSBjYWNoZSBldmVudCBpcyBmb3IgYSByZW1vdGUgY2FjaGUuIFdoZW4gdGhlIGV2ZW50IGlzIGEgYEhJVGAgdGhlIHJlcXVlc3QgYWxzbyBhY2NlcHRzIGEgbnVtYmVyIGBkdXJhdGlvbmAgd2hpY2ggaXMgdGhlIHRpbWUgdGFrZW4gdG8gZ2VuZXJhdGUgdGhlIGFydGlmYWN0IGluIHRoZSBjYWNoZS4nLFxuICAgICAgc3VtbWFyeTogJ1JlY29yZCBhbiBhcnRpZmFjdHMgY2FjaGUgdXNhZ2UgZXZlbnQnLFxuICAgICAgdGFnczogWydhcnRpZmFjdHMnXSxcbiAgICB9XG5cbiAgICBuZXcgYXBpZ2F0ZXdheS5DZm5Eb2N1bWVudGF0aW9uUGFydCh0aGlzLCAnQXJ0aWZhY3RzRXZlbnRzRG9jdW1lbnRhdGlvblBhcnQnLCB7XG4gICAgICBsb2NhdGlvbjoge1xuICAgICAgICB0eXBlOiAnTUVUSE9EJyxcbiAgICAgICAgbWV0aG9kOiAnUE9TVCcsXG4gICAgICAgIHBhdGg6ICcvdjgvYXJ0aWZhY3RzL2V2ZW50cycsXG4gICAgICB9LFxuICAgICAgcHJvcGVydGllczogSlNPTi5zdHJpbmdpZnkocmVjb3JkRXZlbnREb2N1bWVudGF0aW9uUGFydCksXG4gICAgICByZXN0QXBpSWQ6IGFwaS5yZXN0QXBpSWQsXG4gICAgfSk7XG5cbiAgICBjb25zdCBzdGF0dXNSZXNvdXJjZSA9IGFydGlmYWN0c1Jlc291cmNlLmFkZFJlc291cmNlKCdzdGF0dXMnKTtcbiAgICBzdGF0dXNSZXNvdXJjZS5hZGRNZXRob2QoJ0dFVCcsIG5ldyBhcGlnYXRld2F5LkxhbWJkYUludGVncmF0aW9uKHByb3BzLmxhbWJkYUZ1bmN0aW9ucy5zdGF0dXNGdW5jdGlvbiksIHtcbiAgICAgIG9wZXJhdGlvbk5hbWU6ICdzdGF0dXMnLFxuICAgICAgbWV0aG9kUmVzcG9uc2VzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBzdGF0dXNDb2RlOiAnMjAwJyxcbiAgICAgICAgICByZXNwb25zZU1vZGVsczoge1xuICAgICAgICAgICAgJ2FwcGxpY2F0aW9uL2pzb24nOiBuZXcgYXBpZ2F0ZXdheS5Nb2RlbCh0aGlzLCAnU3RhdHVzUmVzcG9uc2VNb2RlbCcsIHtcbiAgICAgICAgICAgICAgcmVzdEFwaTogYXBpLFxuICAgICAgICAgICAgICBjb250ZW50VHlwZTogJ2FwcGxpY2F0aW9uL2pzb24nLFxuICAgICAgICAgICAgICBtb2RlbE5hbWU6ICdTdGF0dXNSZXNwb25zZU1vZGVsJyxcbiAgICAgICAgICAgICAgc2NoZW1hOiB7XG4gICAgICAgICAgICAgICAgdHlwZTogYXBpZ2F0ZXdheS5Kc29uU2NoZW1hVHlwZS5PQkpFQ1QsXG4gICAgICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAgICAgc3RhdHVzOiB7IHR5cGU6IGFwaWdhdGV3YXkuSnNvblNjaGVtYVR5cGUuU1RSSU5HLCBlbnVtOiBbJ2Rpc2FibGVkJywgJ2VuYWJsZWQnLCAnb3Zlcl9saW1pdCcsICdwYXVzZWQnXSB9LFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgcmVxdWlyZWQ6IFsnc3RhdHVzJ10sXG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB9KSxcbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgXSxcbiAgICB9KTtcblxuICAgIGNvbnN0IHN0YXR1c0RvY3VtZW50YXRpb25QYXJ0ID0ge1xuICAgICAgZGVzY3JpcHRpb246ICdDaGVjayB0aGUgc3RhdHVzIG9mIFJlbW90ZSBDYWNoaW5nIGZvciB0aGlzIHByaW5jaXBhbC4gUmV0dXJucyBhIEpTT04tZW5jb2RlZCBzdGF0dXMgaW5kaWNhdGluZyBpZiBSZW1vdGUgQ2FjaGluZyBpcyBlbmFibGVkLCBkaXNhYmxlZCwgb3IgZGlzYWJsZWQgZHVlIHRvIHVzYWdlIGxpbWl0cy4nLFxuICAgICAgc3VtbWFyeTogJ0dldCBzdGF0dXMgb2YgUmVtb3RlIENhY2hpbmcgZm9yIHRoaXMgcHJpbmNpcGFsJyxcbiAgICAgIHRhZ3M6IFsnYXJ0aWZhY3RzJ10sXG4gICAgfVxuXG4gICAgbmV3IGFwaWdhdGV3YXkuQ2ZuRG9jdW1lbnRhdGlvblBhcnQodGhpcywgJ0FydGlmYWN0c1N0YXR1c0RvY3VtZW50YXRpb25QYXJ0Jywge1xuICAgICAgbG9jYXRpb246IHtcbiAgICAgICAgdHlwZTogJ01FVEhPRCcsXG4gICAgICAgIG1ldGhvZDogJ0dFVCcsXG4gICAgICAgIHBhdGg6ICcvdjgvYXJ0aWZhY3RzL3N0YXR1cycsXG4gICAgICB9LFxuICAgICAgcHJvcGVydGllczogSlNPTi5zdHJpbmdpZnkoc3RhdHVzRG9jdW1lbnRhdGlvblBhcnQpLFxuICAgICAgcmVzdEFwaUlkOiBhcGkucmVzdEFwaUlkLFxuICAgIH0pO1xuXG4gICAgY29uc3QgaGFzaFJlc291cmNlID0gYXJ0aWZhY3RzUmVzb3VyY2UuYWRkUmVzb3VyY2UoJ3toYXNofScpO1xuXG4gICAgaGFzaFJlc291cmNlLmFkZE1ldGhvZCgnT1BUSU9OUycsIG5ldyBhcGlnYXRld2F5LkxhbWJkYUludGVncmF0aW9uKHByb3BzLmxhbWJkYUZ1bmN0aW9ucy5wcmVmbGlnaHRBcnRpZmFjdEZ1bmN0aW9uKSwge1xuICAgICAgb3BlcmF0aW9uTmFtZTogJ3ByZWZsaWdodEFydGlmYWN0JyxcbiAgICAgIG1ldGhvZFJlc3BvbnNlczogW3sgc3RhdHVzQ29kZTogJzIwMCcgfV0sXG4gICAgfSk7XG5cbiAgICAvLyBHRVQgL3Y4L2FydGlmYWN0cy97aGFzaH1cbiAgICBnZXRBcnRpZmFjdEludGVncmF0aW9uKHRoaXMsIHtcbiAgICAgIGFydGlmYWN0c0J1Y2tldDogcHJvcHMuYXJ0aWZhY3RzQnVja2V0LFxuICAgICAgczNDcmVkZW50aWFsczogcHJvcHMuczNDcmVkZW50aWFscyxcbiAgICAgIGFwaSxcbiAgICAgIGhhc2hSZXNvdXJjZSxcbiAgICB9KTtcblxuICAgIC8vIEhFQUQgL3Y4L2FydGlmYWN0cy97aGFzaH1cbiAgICAvLyBDaGVjayB0aGF0IGEgY2FjaGUgYXJ0aWZhY3Qgd2l0aCB0aGUgZ2l2ZW4gYGhhc2hgIGV4aXN0cy4gVGhpcyByZXF1ZXN0IHJldHVybnMgcmVzcG9uc2UgaGVhZGVycyBvbmx5XG4gICAgLy8gYW5kIGlzIGVxdWl2YWxlbnQgdG8gYSBgR0VUYCByZXF1ZXN0IHRvIHRoaXMgZW5kcG9pbnQgd2hlcmUgdGhlIHJlc3BvbnNlIGNvbnRhaW5zIG5vIGJvZHkuXG4gICAgaGVhZEFydGlmYWN0SW50ZWdyYXRpb24odGhpcywge1xuICAgICAgYXJ0aWZhY3RzQnVja2V0OiBwcm9wcy5hcnRpZmFjdHNCdWNrZXQsXG4gICAgICBzM0NyZWRlbnRpYWxzOiBwcm9wcy5zM0NyZWRlbnRpYWxzLFxuICAgICAgYXBpLFxuICAgICAgaGFzaFJlc291cmNlLFxuICAgIH0pO1xuXG4gICAgLy8gUFVUIC92OC9hcnRpZmFjdHMve2hhc2h9XG4gICAgLy8gVXBsb2FkcyBhIGNhY2hlIGFydGlmYWN0IGlkZW50aWZpZWQgYnkgdGhlIGBoYXNoYCBzcGVjaWZpZWQgb24gdGhlIHBhdGguXG4gICAgLy8gVGhlIGNhY2hlIGFydGlmYWN0IGNhbiB0aGVuIGJlIGRvd25sb2FkZWQgd2l0aCB0aGUgcHJvdmlkZWQgYGhhc2hgLlxuICAgIHB1dEFydGlmYWN0SW50ZWdyYXRpb24odGhpcywge1xuICAgICAgYXJ0aWZhY3RzQnVja2V0OiBwcm9wcy5hcnRpZmFjdHNCdWNrZXQsXG4gICAgICBzM0NyZWRlbnRpYWxzOiBwcm9wcy5zM0NyZWRlbnRpYWxzLFxuICAgICAgYXBpLFxuICAgICAgaGFzaFJlc291cmNlLFxuICAgIH0pO1xuXG4gICAgLy8gUE9TVCAvdjgvYXJ0aWZhY3RzXG4gICAgLy8gUXVlcnkgaW5mb3JtYXRpb24gYWJvdXQgYW4gYXJyYXkgb2YgYXJ0aWZhY3RzLlxuICAgIGFydGlmYWN0c1Jlc291cmNlLmFkZE1ldGhvZCgnUE9TVCcsIG5ldyBhcGlnYXRld2F5LkxhbWJkYUludGVncmF0aW9uKHByb3BzLmxhbWJkYUZ1bmN0aW9ucy5hcnRpZmFjdFF1ZXJ5RnVuY3Rpb24pLCB7XG4gICAgICBvcGVyYXRpb25OYW1lOiAnYXJ0aWZhY3RRdWVyeScsXG4gICAgICBtZXRob2RSZXNwb25zZXM6IFt7IHN0YXR1c0NvZGU6ICcyMDAnIH1dLFxuICAgICAgcmVxdWVzdE1vZGVsczoge1xuICAgICAgICAnYXBwbGljYXRpb24vanNvbic6IG5ldyBhcGlnYXRld2F5Lk1vZGVsKHRoaXMsICdBcnRpZmFjdFF1ZXJ5TW9kZWwnLCB7XG4gICAgICAgICAgcmVzdEFwaTogYXBpLFxuICAgICAgICAgIGNvbnRlbnRUeXBlOiAnYXBwbGljYXRpb24vanNvbicsXG4gICAgICAgICAgbW9kZWxOYW1lOiAnQXJ0aWZhY3RRdWVyeU1vZGVsJyxcbiAgICAgICAgICBzY2hlbWE6IHtcbiAgICAgICAgICAgIHR5cGU6IGFwaWdhdGV3YXkuSnNvblNjaGVtYVR5cGUuT0JKRUNULFxuICAgICAgICAgICAgcHJvcGVydGllczoge1xuICAgICAgICAgICAgICBoYXNoZXM6IHsgdHlwZTogYXBpZ2F0ZXdheS5Kc29uU2NoZW1hVHlwZS5BUlJBWSwgaXRlbXM6IHsgdHlwZTogYXBpZ2F0ZXdheS5Kc29uU2NoZW1hVHlwZS5TVFJJTkcgfSB9LFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHJlcXVpcmVkOiBbJ2hhc2hlcyddLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pLFxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIGNvbnN0IGFydGlmYWN0UXVlcnlEb2N1bWVudGF0aW9uUGFydCA9IHtcbiAgICAgIGRlc2NyaXB0aW9uOiAnUXVlcnkgaW5mb3JtYXRpb24gYWJvdXQgYW4gYXJyYXkgb2YgYXJ0aWZhY3RzLicsXG4gICAgICBzdW1tYXJ5OiAnUXVlcnkgaW5mb3JtYXRpb24gYWJvdXQgYW4gYXJ0aWZhY3QnLFxuICAgICAgdGFnczogWydhcnRpZmFjdHMnXSxcbiAgICB9XG5cbiAgICBuZXcgYXBpZ2F0ZXdheS5DZm5Eb2N1bWVudGF0aW9uUGFydCh0aGlzLCAnQXJ0aWZhY3RzUXVlcnlEb2N1bWVudGF0aW9uUGFydCcsIHtcbiAgICAgIGxvY2F0aW9uOiB7XG4gICAgICAgIHR5cGU6ICdNRVRIT0QnLFxuICAgICAgICBtZXRob2Q6ICdQT1NUJyxcbiAgICAgICAgcGF0aDogJy92OC9hcnRpZmFjdHMnLFxuICAgICAgfSxcbiAgICAgIHByb3BlcnRpZXM6IEpTT04uc3RyaW5naWZ5KGFydGlmYWN0UXVlcnlEb2N1bWVudGF0aW9uUGFydCksXG4gICAgICByZXN0QXBpSWQ6IGFwaS5yZXN0QXBpSWQsXG4gICAgfSk7XG5cbiAgICAvLyB0dXJibyBsb2dpblxuICAgIC8vIFRPRE86IGltcGxlbWVudCB0dXJibyBsb2dpbiBmb3IgdGhpcmQgcGFydHkgSldUc1xuICAgIC8vIGNvbnN0IHR1cmJvcmVwb1Jlc291cmNlID0gYXBpLnJvb3QuYWRkUmVzb3VyY2UoJ3R1cmJvcmVwbycpO1xuICAgIC8vIGNvbnN0IHRva2VuUmVzb3VyY2UgPSB0dXJib3JlcG9SZXNvdXJjZS5hZGRSZXNvdXJjZSgndG9rZW4nKTtcblxuICAgIC8vIC8vIEdFVCAvdjgvdHVyYm9yZXBvL3Rva2VuXG4gICAgLy8gdG9rZW5SZXNvdXJjZS5hZGRNZXRob2QoJ0dFVCcsIG5ldyBhcGlnYXRld2F5LkxhbWJkYUludGVncmF0aW9uKHByb3BzLmxhbWJkYUZ1bmN0aW9ucy5pbml0aWF0ZUxvZ2luRnVuY3Rpb24pLCB7XG4gICAgLy8gICBvcGVyYXRpb25OYW1lOiAnaW5pdGlhdGVMb2dpbicsXG4gICAgLy8gfSk7XG5cbiAgICAvLyBjb25zdCBpbml0aWF0ZUxvZ2luRG9jdW1lbnRhdGlvblBhcnQgPSB7XG4gICAgLy8gICBkZXNjcmlwdGlvbjogJ0luaXRpYXRlcyBhIGxvZ2luIHByb2Nlc3MgZm9yIFR1cmJvcmVwby4nLFxuICAgIC8vICAgc3VtbWFyeTogJ0luaXRpYXRlIGxvZ2luJyxcbiAgICAvLyAgIHRhZ3M6IFsnbG9naW4nXSxcbiAgICAvLyB9XG5cbiAgICAvLyBuZXcgYXBpZ2F0ZXdheS5DZm5Eb2N1bWVudGF0aW9uUGFydCh0aGlzLCAnVHVyYm9yZXBvSW5pdGlhdGVMb2dpbkRvY3VtZW50YXRpb25QYXJ0Jywge1xuICAgIC8vICAgbG9jYXRpb246IHtcbiAgICAvLyAgICAgdHlwZTogJ01FVEhPRCcsXG4gICAgLy8gICAgIG1ldGhvZDogJ0dFVCcsXG4gICAgLy8gICAgIHBhdGg6ICcvdjgvdHVyYm9yZXBvL3Rva2VuJyxcbiAgICAvLyAgIH0sXG4gICAgLy8gICBwcm9wZXJ0aWVzOiBKU09OLnN0cmluZ2lmeShpbml0aWF0ZUxvZ2luRG9jdW1lbnRhdGlvblBhcnQpLFxuICAgIC8vICAgcmVzdEFwaUlkOiBhcGkucmVzdEFwaUlkLFxuICAgIC8vIH0pO1xuXG4gICAgLy8gLy8gR0VUIC92OC90dXJib3JlcG8vc3VjY2Vzc1xuICAgIC8vIGNvbnN0IHN1Y2Nlc3NSZXNvdXJjZSA9IHR1cmJvcmVwb1Jlc291cmNlLmFkZFJlc291cmNlKCdzdWNjZXNzJyk7XG4gICAgLy8gc3VjY2Vzc1Jlc291cmNlLmFkZE1ldGhvZCgnR0VUJywgbmV3IGFwaWdhdGV3YXkuTGFtYmRhSW50ZWdyYXRpb24ocHJvcHMubGFtYmRhRnVuY3Rpb25zLmxvZ2luU3VjY2Vzc0Z1bmN0aW9uKSwge1xuICAgIC8vICAgb3BlcmF0aW9uTmFtZTogJ2xvZ2luU3VjY2VzcycsXG4gICAgLy8gfSk7XG5cbiAgICAvLyBjb25zdCBsb2dpblN1Y2Nlc3NEb2N1bWVudGF0aW9uUGFydCA9IHtcbiAgICAvLyAgIGRlc2NyaXB0aW9uOiAnSGFuZGxlcyB0aGUgc3VjY2VzcyBvZiBhIGxvZ2luIHByb2Nlc3MgZm9yIFR1cmJvcmVwby4nLFxuICAgIC8vICAgc3VtbWFyeTogJ0xvZ2luIHN1Y2Nlc3MnLFxuICAgIC8vICAgdGFnczogWydsb2dpbiddLFxuICAgIC8vIH1cblxuICAgIC8vIG5ldyBhcGlnYXRld2F5LkNmbkRvY3VtZW50YXRpb25QYXJ0KHRoaXMsICdUdXJib3JlcG9Mb2dpblN1Y2Nlc3NEb2N1bWVudGF0aW9uUGFydCcsIHtcbiAgICAvLyAgIGxvY2F0aW9uOiB7XG4gICAgLy8gICAgIHR5cGU6ICdNRVRIT0QnLFxuICAgIC8vICAgICBtZXRob2Q6ICdHRVQnLFxuICAgIC8vICAgICBwYXRoOiAnL3Y4L3R1cmJvcmVwby9zdWNjZXNzJyxcbiAgICAvLyAgIH0sXG4gICAgLy8gICBwcm9wZXJ0aWVzOiBKU09OLnN0cmluZ2lmeShsb2dpblN1Y2Nlc3NEb2N1bWVudGF0aW9uUGFydCksXG4gICAgLy8gICByZXN0QXBpSWQ6IGFwaS5yZXN0QXBpSWQsXG4gICAgLy8gfSk7XG5cbiAgICBjb25zdCB2MlJlc291cmNlID0gYXBpLnJvb3QuYWRkUmVzb3VyY2UoJ3YyJyk7XG5cbiAgICBjb25zdCB1c2VyUmVzb3VyY2UgPSB2MlJlc291cmNlLmFkZFJlc291cmNlKCd1c2VyJyk7XG5cbiAgICAvLyBHRVQgL3YyL3VzZXJcbiAgICB1c2VyUmVzb3VyY2UuYWRkTWV0aG9kKCdHRVQnLCBuZXcgYXBpZ2F0ZXdheS5MYW1iZGFJbnRlZ3JhdGlvbihwcm9wcy5sYW1iZGFGdW5jdGlvbnMuZ2V0VXNlckluZm9GdW5jdGlvbiksIHtcbiAgICAgIG9wZXJhdGlvbk5hbWU6ICdnZXRVc2VySW5mbycsXG4gICAgfSk7XG5cbiAgICBjb25zdCBnZXRVc2VySW5mb0RvY3VtZW50YXRpb25QYXJ0ID0ge1xuICAgICAgZGVzY3JpcHRpb246ICdSZXRyaWV2ZXMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGF1dGhlbnRpY2F0ZWQgdXNlci4nLFxuICAgICAgc3VtbWFyeTogJ0dldCB1c2VyIGluZm8nLFxuICAgICAgdGFnczogWydsb2dpbiddLFxuICAgIH1cblxuICAgIG5ldyBhcGlnYXRld2F5LkNmbkRvY3VtZW50YXRpb25QYXJ0KHRoaXMsICdUdXJib3JlcG9Vc2VySW5mb0RvY3VtZW50YXRpb25QYXJ0Jywge1xuICAgICAgbG9jYXRpb246IHtcbiAgICAgICAgdHlwZTogJ01FVEhPRCcsXG4gICAgICAgIG1ldGhvZDogJ0dFVCcsXG4gICAgICAgIHBhdGg6ICcvdjIvdXNlcicsXG4gICAgICB9LFxuICAgICAgcHJvcGVydGllczogSlNPTi5zdHJpbmdpZnkoZ2V0VXNlckluZm9Eb2N1bWVudGF0aW9uUGFydCksXG4gICAgICByZXN0QXBpSWQ6IGFwaS5yZXN0QXBpSWQsXG4gICAgfSk7XG5cbiAgICAvLyBjbG91ZGZyb250IGRvbWFpbiBuYW1lIGZvciBDTkFNRVxuICAgIG5ldyBjZGsuQ2ZuT3V0cHV0KHRoaXMsICdDbG91ZGZyb250QWxpYXNEb21haW5OYW1lJywge1xuICAgICAgdmFsdWU6IGFwaS5kb21haW5OYW1lPy5kb21haW5OYW1lQWxpYXNEb21haW5OYW1lID8/ICdOL0EnLFxuICAgIH0pO1xuICB9XG59Il19