@tsclean/core
Version:
Plugin for API Rest Full development, based on Clean Architecture, IoC and Dependency Injection.
195 lines • 38.3 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RouterExplorer = void 0;
const path_to_regexp_1 = __importDefault(require("path-to-regexp"));
const errors_1 = require("../errors");
const helpers_1 = require("../helpers");
const interceptors_1 = require("../interceptors");
const request_constants_1 = require("./request/request-constants");
const route_params_factory_1 = require("./route-params-factory");
const router_execution_context_1 = require("./router-execution-context");
const enums_1 = require("../enums");
const services_1 = require("../services");
const core_1 = require("../core");
const access_1 = require("../access");
const utils_1 = require("../utils");
const exceptions_1 = require("../exceptions");
class RouterExplorer {
constructor(metadataScanner, container, graphInspector, injector, routerProxy, exceptionsFilter, config, routePathFactory) {
this.metadataScanner = metadataScanner;
this.container = container;
this.graphInspector = graphInspector;
this.injector = injector;
this.routerProxy = routerProxy;
this.exceptionsFilter = exceptionsFilter;
this.routePathFactory = routePathFactory;
this.routerMethodFactory = new helpers_1.RouterMethodFactory();
this.logger = new services_1.Logger(null, { timestamp: true });
this.exceptionFiltersCache = new WeakMap();
const routeParamsFactory = new route_params_factory_1.RouteParamsFactory();
const pipesContextCreator = new core_1.HandlersContextCreator(container, config);
const pipesConsumer = new core_1.HandlersConsumer();
const guardsContextCreator = new access_1.AccessResourceContextCreator(container, config);
const guardsConsumer = new access_1.AccessResourceConsumer();
const interceptorsContextCreator = new interceptors_1.InterceptorsContextCreator(container, config);
const interceptorsConsumer = new interceptors_1.InterceptorsConsumer();
this.executionContextCreator = new router_execution_context_1.RouterExecutionContext(routeParamsFactory, pipesContextCreator, pipesConsumer, guardsContextCreator, guardsConsumer, interceptorsContextCreator, interceptorsConsumer, container.getHttpAdapterRef());
}
explore(instanceWrapper, moduleKey, applicationRef, host, routePathMetadata) {
const { instance } = instanceWrapper;
const routerPaths = this.scanForPaths(instance);
this.applyPathsToRouterProxy(applicationRef, routerPaths, instanceWrapper, moduleKey, routePathMetadata, host);
}
extractRouterPath(metaType) {
const path = Reflect.getMetadata(helpers_1.PATH_METADATA, metaType);
if ((0, utils_1.isUndefined)(path))
throw new errors_1.UnknownRequestMappingException();
if (Array.isArray(path))
return path.map(p => (0, utils_1.addLeadingSlash)(p));
return [(0, utils_1.addLeadingSlash)(path)];
}
scanForPaths(instance, prototype) {
const instancePrototype = (0, utils_1.isUndefined)(prototype)
? Object.getPrototypeOf(instance)
: prototype;
return this.metadataScanner.scanFromPrototype(instance, instancePrototype, method => this.exploreMethodMetadata(instance, instancePrototype, method));
}
exploreMethodMetadata(instance, prototype, methodName) {
const instanceCallback = instance[methodName];
const prototypeCallback = prototype[methodName];
const routePath = Reflect.getMetadata(helpers_1.PATH_METADATA, prototypeCallback);
if ((0, utils_1.isUndefined)(routePath))
return undefined;
const requestMethod = Reflect.getMetadata(helpers_1.METHOD_METADATA, prototypeCallback);
const path = (0, utils_1.isString)(routePath)
? [(0, utils_1.addLeadingSlash)(routePath)]
: routePath.map((p) => (0, utils_1.addLeadingSlash)(p));
return {
path,
requestMethod,
targetCallback: instanceCallback,
methodName
};
}
applyPathsToRouterProxy(router, routeDefinitions, instanceWrapper, moduleKey, routePathMetadata, host) {
;
(routeDefinitions || []).forEach(routeDefinition => {
const { version: methodVersion } = routeDefinition;
routePathMetadata.methodVersion = methodVersion;
this.applyCallbackToRouter(router, routeDefinition, instanceWrapper, moduleKey, routePathMetadata, host);
});
}
applyCallbackToRouter(router, routeDefinition, instanceWrapper, moduleKey, routePathMetadata, host) {
const { path: paths, requestMethod, targetCallback, methodName } = routeDefinition;
const { instance } = instanceWrapper;
const routerMethodRef = this.routerMethodFactory
.get(router, requestMethod)
.bind(router);
const isRequestScoped = !instanceWrapper.isDependencyTreeStatic();
const proxy = isRequestScoped
? this.createRequestScopedHandler(instanceWrapper, requestMethod, this.container.getModuleByKey(moduleKey), moduleKey, methodName)
: this.createCallbackProxy(instance, targetCallback, methodName, moduleKey, requestMethod);
let routeHandler = this.applyHostFilter(host, proxy);
paths.forEach(path => {
routePathMetadata.methodPath = path;
const pathsToRegister = this.routePathFactory.create(routePathMetadata, requestMethod);
pathsToRegister.forEach(path => {
const entrypointDefinition = {
type: 'http-endpoint',
methodName,
className: instanceWrapper.name,
classNodeId: instanceWrapper.id,
metadata: {
key: path,
path,
requestMethod: enums_1.RequestMethod[requestMethod]
}
};
this.copyMetadataToCallback(targetCallback, routeHandler);
routerMethodRef(path, routeHandler);
this.graphInspector.insertEntrypointDefinition(entrypointDefinition, instanceWrapper.id);
});
const pathsToLog = this.routePathFactory.create(Object.assign(Object.assign({}, routePathMetadata), { versioningOptions: undefined }), requestMethod);
pathsToLog.forEach(path => this.logger.log((0, helpers_1.ROUTE_MAPPED_MESSAGE)(path, requestMethod)));
});
}
applyHostFilter(host, handler) {
if (!host)
return handler;
const httpAdapterRef = this.container.getHttpAdapterRef();
const hosts = Array.isArray(host) ? host : [host];
const hostRegExps = hosts.map((host) => {
const keys = [];
const regexp = (0, path_to_regexp_1.default)(host, keys);
return { regexp, keys };
});
const unsupportedFilteringErrorMessage = Array.isArray(host)
? `HTTP adapter does not support filtering on hosts: ["${host.join('", "')}"]`
: `HTTP adapter does not support filtering on host: "${host}"`;
return (req, res, next) => {
;
req.hosts = {};
const hostname = httpAdapterRef.getRequestHostname(req) || '';
for (const exp of hostRegExps) {
const match = hostname.match(exp.regexp);
if (match) {
exp.keys.forEach((key, i) => (req.hosts[key.name] = match[i + 1]));
return handler(req, res, next);
}
}
if (!next)
throw new exceptions_1.InternalServerErrorException(unsupportedFilteringErrorMessage);
return next();
};
}
createCallbackProxy(instance, callback, methodName, moduleRef, requestMethod, contextId = core_1.STATIC_CONTEXT, inquirerId) {
var _a;
const executionContext = this.executionContextCreator.create(instance, callback, methodName, moduleRef, requestMethod, contextId, inquirerId);
const exceptionFilter = this.exceptionsFilter.create(instance, callback, moduleRef, contextId, inquirerId);
return (_a = this.routerProxy) === null || _a === void 0 ? void 0 : _a.createProxy(executionContext, exceptionFilter);
}
createRequestScopedHandler(instanceWrapper, requestMethod, moduleRef, moduleKey, methodName) {
const { instance } = instanceWrapper;
const collection = moduleRef.controllers;
return async (req, res, next) => {
var _a, _b;
try {
const contextId = this.getContextId(req);
const contextInstance = await ((_a = this.injector) === null || _a === void 0 ? void 0 : _a.loadPerContext(instance, moduleRef, collection, contextId));
await this.createCallbackProxy(contextInstance, contextInstance[methodName], methodName, moduleKey, requestMethod, contextId, instanceWrapper.id)(req, res, next);
}
catch (err) {
let exceptionFilter = this.exceptionFiltersCache.get(instance[methodName]);
if (!exceptionFilter) {
exceptionFilter = (_b = this.exceptionsFilter) === null || _b === void 0 ? void 0 : _b.create(instance, instance[methodName], moduleKey);
this.exceptionFiltersCache.set(instance[methodName], exceptionFilter);
}
const host = new helpers_1.ExecutionContextHost([req, res, next]);
exceptionFilter.next(err, host);
}
};
}
getContextId(request) {
const contextId = helpers_1.ContextIdFactory.getByRequest(request);
if (!request[request_constants_1.REQUEST_CONTEXT_ID]) {
Object.defineProperty(request, request_constants_1.REQUEST_CONTEXT_ID, {
value: contextId,
enumerable: false,
writable: false,
configurable: false
});
this.container.registerRequestProvider(request, contextId);
}
return contextId;
}
copyMetadataToCallback(originalCallback, targetCallback) {
for (const key of Reflect.getMetadataKeys(originalCallback)) {
Reflect.defineMetadata(key, Reflect.getMetadata(key, originalCallback), targetCallback);
}
}
}
exports.RouterExplorer = RouterExplorer;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGVyLWV4cGxvcmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3JvdXRlci9yb3V0ZXItZXhwbG9yZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsb0VBQXlDO0FBQ3pDLHNDQUEwRDtBQUMxRCx3Q0FPbUI7QUFDbkIsa0RBR3dCO0FBQ3hCLG1FQUFnRTtBQUNoRSxpRUFBMkQ7QUFFM0QseUVBQW1FO0FBRW5FLG9DQUF3QztBQVF4QywwQ0FBb0M7QUFFcEMsa0NBU2dCO0FBQ2hCLHNDQUFnRjtBQUVoRixvQ0FBaUU7QUFDakUsOENBQTREO0FBTzVELE1BQWEsY0FBYztJQU16QixZQUNtQixlQUFnQyxFQUNoQyxTQUF1QixFQUN2QixjQUE4QixFQUM5QixRQUFtQixFQUNuQixXQUF5QixFQUN6QixnQkFBNEMsRUFDN0QsTUFBMEIsRUFDVCxnQkFBbUM7UUFQbkMsb0JBQWUsR0FBZixlQUFlLENBQWlCO1FBQ2hDLGNBQVMsR0FBVCxTQUFTLENBQWM7UUFDdkIsbUJBQWMsR0FBZCxjQUFjLENBQWdCO1FBQzlCLGFBQVEsR0FBUixRQUFRLENBQVc7UUFDbkIsZ0JBQVcsR0FBWCxXQUFXLENBQWM7UUFDekIscUJBQWdCLEdBQWhCLGdCQUFnQixDQUE0QjtRQUU1QyxxQkFBZ0IsR0FBaEIsZ0JBQWdCLENBQW1CO1FBWnJDLHdCQUFtQixHQUFHLElBQUksNkJBQW1CLEVBQUUsQ0FBQTtRQUMvQyxXQUFNLEdBQUcsSUFBSSxpQkFBTSxDQUFDLElBQUksRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFBO1FBQzlDLDBCQUFxQixHQUFHLElBQUksT0FBTyxFQUFFLENBQUE7UUFZcEQsTUFBTSxrQkFBa0IsR0FBRyxJQUFJLHlDQUFrQixFQUFFLENBQUE7UUFDbkQsTUFBTSxtQkFBbUIsR0FBRyxJQUFJLDZCQUFzQixDQUFDLFNBQVMsRUFBRSxNQUFNLENBQUMsQ0FBQTtRQUN6RSxNQUFNLGFB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LENBQUMsY0FBYyxDQUNwQixHQUFHLEVBQ0gsT0FBTyxDQUFDLFdBQVcsQ0FBQyxHQUFHLEVBQUUsZ0JBQWdCLENBQUMsRUFDMUMsY0FBYyxDQUNmLENBQUE7UUFDSCxDQUFDO0lBQ0gsQ0FBQztDQUNGO0FBNVdELHdDQTRXQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBwYXRoVG9SZWdleHAgZnJvbSAncGF0aC10by1yZWdleHAnXG5pbXBvcnQgeyBVbmtub3duUmVxdWVzdE1hcHBpbmdFeGNlcHRpb24gfSBmcm9tICcuLi9lcnJvcnMnXG5pbXBvcnQge1xuICBST1VURV9NQVBQRURfTUVTU0FHRSxcbiAgQ29udGV4dElkRmFjdG9yeSxcbiAgRXhlY3V0aW9uQ29udGV4dEhvc3QsXG4gIFJvdXRlck1ldGhvZEZhY3RvcnksXG4gIFBBVEhfTUVUQURBVEEsXG4gIE1FVEhPRF9NRVRBREFUQVxufSBmcm9tICcuLi9oZWxwZXJzJ1xuaW1wb3J0IHtcbiAgSW50ZXJjZXB0b3JzQ29uc3VtZXIsXG4gIEludGVyY2VwdG9yc0NvbnRleHRDcmVhdG9yXG59IGZyb20gJy4uL2ludGVyY2VwdG9ycydcbmltcG9ydCB7IFJFUVVFU1RfQ09OVEVYVF9JRCB9IGZyb20gJy4vcmVxdWVzdC9yZXF1ZXN0LWNvbnN0YW50cydcbmltcG9ydCB7IFJvdXRlUGFyYW1zRmFjdG9yeSB9IGZyb20gJy4vcm91dGUtcGFyYW1zLWZhY3RvcnknXG5pbXBvcnQgeyBSb3V0ZVBhdGhGYWN0b3J5IH0gZnJvbSAnLi9yb3V0ZS1wYXRoLWZhY3RvcnknXG5pbXBvcnQgeyBSb3V0ZXJFeGVjdXRpb25Db250ZXh0IH0gZnJvbSAnLi9yb3V0ZXItZXhlY3V0aW9uLWNvbnRleHQnXG5pbXBvcnQgeyBSb3V0ZXJQcm94eSwgUm91dGVyUHJveHlDYWxsYmFjayB9IGZyb20gJy4vcm91dGVyLXByb3h5J1xuaW1wb3J0IHsgUmVxdWVzdE1ldGhvZCB9IGZyb20gJy4uL2VudW1zJ1xuaW1wb3J0IHtcbiAgRXhjZXB0aW9uc0ZpbHRlckludGVyZmFjZSxcbiAgSHR0cFNlcnZlcixcbiAgUm91dGVEZWZpbml0aW9uSW50ZXJmYWNlLFxuICBSb3V0ZVBhdGhNZXRhZGF0YUludGVyZmFjZSxcbiAgVHlwZVxufSBmcm9tICcuLi9jb250cmFjdHMnXG5pbXBvcnQgeyBMb2dnZXIgfSBmcm9tICcuLi9zZXJ2aWNlcydcbmltcG9ydCB7IEFwcGxpY2F0aW9uQ29uZmlnLCBNZXRhZGF0YVNjYW5uZXIgfSBmcm9tICcuLi9hcHAnXG5pbXBvcnQge1xuICBDb250ZXh0SWQsXG4gIEluamVjdG9yLFxuICBJbnN0YW5jZVdyYXBwZXIsXG4gIE1vZHVsZSxcbiAgQ29udGFpbmVySW9DLFxuICBIYW5kbGVyc0NvbnN1bWVyLFxuICBIYW5kbGVyc0NvbnRleHRDcmVhdG9yLFxuICBTVEFUSUNfQ09OVEVYVFxufSBmcm9tICcuLi9jb3JlJ1xuaW1wb3J0IHsgQWNjZXNzUmVzb3VyY2VDb25zdW1lciwgQWNjZXNzUmVzb3VyY2VDb250ZXh0Q3JlYXRvciB9IGZyb20gJy4uL2FjY2VzcydcbmltcG9ydCB7IENvbnRyb2xsZXJUeXBlIH0gZnJvbSAnLi4vdHlwZXMnXG5pbXBvcnQgeyBhZGRMZWFkaW5nU2xhc2gsIGlzU3RyaW5nLCBpc1VuZGVmaW5lZCB9IGZyb20gJy4uL3V0aWxzJ1xuaW1wb3J0IHsgSW50ZXJuYWxTZXJ2ZXJFcnJvckV4Y2VwdGlvbiB9IGZyb20gJy4uL2V4Y2VwdGlvbnMnXG5pbXBvcnQge1xuICBFbnRyeXBvaW50LFxuICBHcmFwaEluc3BlY3RvcixcbiAgSHR0cEVudHJ5cG9pbnRNZXRhZGF0YVxufSBmcm9tICcuLi9pbnNwZWN0b3InXG5cbmV4cG9ydCBjbGFzcyBSb3V0ZXJFeHBsb3JlciB7XG4gIHByaXZhdGUgcmVhZG9ubHkgZXhlY3V0aW9uQ29udGV4dENyZWF0b3I6IFJvdXRlckV4ZWN1dGlvbkNvbnRleHRcbiAgcHJpdmF0ZSByZWFkb25seSByb3V0ZXJNZXRob2RGYWN0b3J5ID0gbmV3IFJvdXRlck1ldGhvZEZhY3RvcnkoKVxuICBwcml2YXRlIHJlYWRvbmx5IGxvZ2dlciA9IG5ldyBMb2dnZXIobnVsbCwgeyB0aW1lc3RhbXA6IHRydWUgfSlcbiAgcHJpdmF0ZSByZWFkb25seSBleGNlcHRpb25GaWx0ZXJzQ2FjaGUgPSBuZXcgV2Vha01hcCgpXG5cbiAgY29uc3RydWN0b3IgKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgbWV0YWRhdGFTY2FubmVyOiBNZXRhZGF0YVNjYW5uZXIsXG4gICAgcHJpdmF0ZSByZWFkb25seSBjb250YWluZXI6IENvbnRhaW5lcklvQyxcbiAgICBwcml2YXRlIHJlYWRvbmx5IGdyYXBoSW5zcGVjdG9yOiBHcmFwaEluc3BlY3RvcixcbiAgICBwcml2YXRlIHJlYWRvbmx5IGluamVjdG9yPzogSW5qZWN0b3IsXG4gICAgcHJpdmF0ZSByZWFkb25seSByb3V0ZXJQcm94eT86IFJvdXRlclByb3h5LFxuICAgIHByaXZhdGUgcmVhZG9ubHkgZXhjZXB0aW9uc0ZpbHRlcj86IEV4Y2VwdGlvbnNGaWx0ZXJJbnRlcmZhY2UsXG4gICAgY29uZmlnPzogQXBwbGljYXRpb25Db25maWcsXG4gICAgcHJpdmF0ZSByZWFkb25seSByb3V0ZVBhdGhGYWN0b3J5PzogUm91dGVQYXRoRmFjdG9yeSxcbiAgKSB7XG4gICAgY29uc3Qgcm91dGVQYXJhbXNGYWN0b3J5ID0gbmV3IFJvdXRlUGFyYW1zRmFjdG9yeSgpXG4gICAgY29uc3QgcGlwZXNDb250ZXh0Q3JlYXRvciA9IG5ldyBIYW5kbGVyc0NvbnRleHRDcmVhdG9yKGNvbnRhaW5lciwgY29uZmlnKVxuICAgIGNvbnN0IHBpcGVzQ29uc3VtZXIgPSBuZXcgSGFuZGxlcnNDb25zdW1lcigpXG4gICAgY29uc3QgZ3VhcmRzQ29udGV4dENyZWF0b3IgPSBuZXcgQWNjZXNzUmVzb3VyY2VDb250ZXh0Q3JlYXRvcihcbiAgICAgIGNvbnRhaW5lcixcbiAgICAgIGNvbmZpZ1xuICAgIClcbiAgICBjb25zdCBndWFyZHNDb25zdW1lciA9IG5ldyBBY2Nlc3NSZXNvdXJjZUNvbnN1bWVyKClcbiAgICBjb25zdCBpbnRlcmNlcHRvcnNDb250ZXh0Q3JlYXRvciA9IG5ldyBJbnRlcmNlcHRvcnNDb250ZXh0Q3JlYXRvcihcbiAgICAgIGNvbnRhaW5lcixcbiAgICAgIGNvbmZpZ1xuICAgIClcbiAgICBjb25zdCBpbnRlcmNlcHRvcnNDb25zdW1lciA9IG5ldyBJbnRlcmNlcHRvcnNDb25zdW1lcigpXG5cbiAgICB0aGlzLmV4ZWN1dGlvbkNvbnRleHRDcmVhdG9yID0gbmV3IFJvdXRlckV4ZWN1dGlvbkNvbnRleHQoXG4gICAgICByb3V0ZVBhcmFtc0ZhY3RvcnksXG4gICAgICBwaXBlc0NvbnRleHRDcmVhdG9yLFxuICAgICAgcGlwZXNDb25zdW1lcixcbiAgICAgIGd1YXJkc0NvbnRleHRDcmVhdG9yLFxuICAgICAgZ3VhcmRzQ29uc3VtZXIsXG4gICAgICBpbnRlcmNlcHRvcnNDb250ZXh0Q3JlYXRvcixcbiAgICAgIGludGVyY2VwdG9yc0NvbnN1bWVyLFxuICAgICAgY29udGFpbmVyLmdldEh0dHBBZGFwdGVyUmVmKClcbiAgICApXG4gIH1cblxuICBwdWJsaWMgZXhwbG9yZTxUIGV4dGVuZHMgSHR0cFNlcnZlciA9IGFueT4gKFxuICAgIGluc3RhbmNlV3JhcHBlcjogSW5zdGFuY2VXcmFwcGVyLFxuICAgIG1vZHVsZUtleTogc3RyaW5nLFxuICAgIGFwcGxpY2F0aW9uUmVmOiBULFxuICAgIGhvc3Q6IHN0cmluZyB8IFJlZ0V4cCB8IEFycmF5PHN0cmluZyB8IFJlZ0V4cD4gfCB1bmRlZmluZWQsXG4gICAgcm91dGVQYXRoTWV0YWRhdGE6IFJvdXRlUGF0aE1ldGFkYXRhSW50ZXJmYWNlXG4gICkge1xuICAgIGNvbnN0IHsgaW5zdGFuY2UgfSA9IGluc3RhbmNlV3JhcHBlclxuICAgIGNvbnN0IHJvdXRlclBhdGhzID0gdGhpcy5zY2FuRm9yUGF0aHMoaW5zdGFuY2UpXG4gICAgdGhpcy5hcHBseVBhdGhzVG9Sb3V0ZXJQcm94eShcbiAgICAgIGFwcGxpY2F0aW9uUmVmLFxuICAgICAgcm91dGVyUGF0aHMsXG4gICAgICBpbnN0YW5jZVdyYXBwZXIsXG4gICAgICBtb2R1bGVLZXksXG4gICAgICByb3V0ZVBhdGhNZXRhZGF0YSxcbiAgICAgIGhvc3RcbiAgICApXG4gIH1cblxuICBwdWJsaWMgZXh0cmFjdFJvdXRlclBhdGggKG1ldGFUeXBlOiBUeXBlPENvbnRyb2xsZXJUeXBlPik6IHN0cmluZ1tdIHtcbiAgICBjb25zdCBwYXRoID0gUmVmbGVjdC5nZXRNZXRhZGF0YShQQVRIX01FVEFEQVRBLCBtZXRhVHlwZSlcbiAgICBpZiAoaXNVbmRlZmluZWQocGF0aCkpIHRocm93IG5ldyBVbmtub3duUmVxdWVzdE1hcHBpbmdFeGNlcHRpb24oKVxuICAgIGlmIChBcnJheS5pc0FycmF5KHBhdGgpKSByZXR1cm4gcGF0aC5tYXAocCA9PiBhZGRMZWFkaW5nU2xhc2gocCkpXG5cbiAgICByZXR1cm4gW2FkZExlYWRpbmdTbGFzaChwYXRoKV1cbiAgfVxuXG4gIHB1YmxpYyBzY2FuRm9yUGF0aHMgKFxuICAgIGluc3RhbmNlOiBDb250cm9sbGVyVHlwZSxcbiAgICBwcm90b3R5cGU/OiBvYmplY3RcbiAgKTogUm91dGVEZWZpbml0aW9uSW50ZXJmYWNlW10ge1xuICAgIGNvbnN0IGluc3RhbmNlUHJvdG90eXBlID0gaXNVbmRlZmluZWQocHJvdG90eXBlKVxuICAgICAgPyBPYmplY3QuZ2V0UHJvdG90eXBlT2YoaW5zdGFuY2UpXG4gICAgICA6IHByb3RvdHlwZVxuXG4gICAgcmV0dXJuIHRoaXMubWV0YWRhdGFTY2FubmVyLnNjYW5Gcm9tUHJvdG90eXBlPFxuICAgICAgQ29udHJvbGxlclR5cGUsXG4gICAgICBSb3V0ZURlZmluaXRpb25JbnRlcmZhY2VcbiAgICA+KGluc3RhbmNlLCBpbnN0YW5jZVByb3RvdHlwZSwgbWV0aG9kID0+XG4gICAgICB0aGlzLmV4cGxvcmVNZXRob2RNZXRhZGF0YShpbnN0YW5jZSwgaW5zdGFuY2VQcm90b3R5cGUsIG1ldGhvZClcbiAgICApXG4gIH1cblxuICBwdWJsaWMgZXhwbG9yZU1ldGhvZE1ldGFkYXRhIChcbiAgICBpbnN0YW5jZTogQ29udHJvbGxlclR5cGUsXG4gICAgcHJvdG90eXBlOiBvYmplY3QsXG4gICAgbWV0aG9kTmFtZTogc3RyaW5nXG4gICk6IFJvdXRlRGVmaW5pdGlvbkludGVyZmFjZSB7XG4gICAgY29uc3QgaW5zdGFuY2VDYWxsYmFjayA9IGluc3RhbmNlW21ldGhvZE5hbWVdXG4gICAgY29uc3QgcHJvdG90eXBlQ2FsbGJhY2sgPSBwcm90b3R5cGVbbWV0aG9kTmFtZV1cbiAgICBjb25zdCByb3V0ZVBhdGggPSBSZWZsZWN0LmdldE1ldGFkYXRhKFBBVEhfTUVUQURBVEEsIHByb3RvdHlwZUNhbGxiYWNrKVxuICAgIGlmIChpc1VuZGVmaW5lZChyb3V0ZVBhdGgpKSByZXR1cm4gdW5kZWZpbmVkXG5cbiAgICBjb25zdCByZXF1ZXN0TWV0aG9kOiBSZXF1ZXN0TWV0aG9kID0gUmVmbGVjdC5nZXRNZXRhZGF0YShcbiAgICAgIE1FVEhPRF9NRVRBREFUQSxcbiAgICAgIHByb3RvdHlwZUNhbGxiYWNrXG4gICAgKVxuICAgIGNvbnN0IHBhdGggPSBpc1N0cmluZyhyb3V0ZVBhdGgpXG4gICAgICA/IFthZGRMZWFkaW5nU2xhc2gocm91dGVQYXRoKV1cbiAgICAgIDogcm91dGVQYXRoLm1hcCgocDogc3RyaW5nKSA9PiBhZGRMZWFkaW5nU2xhc2gocCkpXG5cbiAgICByZXR1cm4ge1xuICAgICAgcGF0aCxcbiAgICAgIHJlcXVlc3RNZXRob2QsXG4gICAgICB0YXJnZXRDYWxsYmFjazogaW5zdGFuY2VDYWxsYmFjayxcbiAgICAgIG1ldGhvZE5hbWVcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgYXBwbHlQYXRoc1RvUm91dGVyUHJveHk8VCBleHRlbmRzIEh0dHBTZXJ2ZXI+IChcbiAgICByb3V0ZXI6IFQsXG4gICAgcm91dGVEZWZpbml0aW9uczogUm91dGVEZWZpbml0aW9uSW50ZXJmYWNlW10sXG4gICAgaW5zdGFuY2VXcmFwcGVyOiBJbnN0YW5jZVdyYXBwZXIsXG4gICAgbW9kdWxlS2V5OiBzdHJpbmcsXG4gICAgcm91dGVQYXRoTWV0YWRhdGE6IFJvdXRlUGF0aE1ldGFkYXRhSW50ZXJmYWNlLFxuICAgIGhvc3Q6IHN0cmluZyB8IFJlZ0V4cCB8IEFycmF5PHN0cmluZyB8IFJlZ0V4cD4gfCB1bmRlZmluZWRcbiAgKSB7XG4gICAgOyhyb3V0ZURlZmluaXRpb25zIHx8IFtdKS5mb3JFYWNoKHJvdXRlRGVmaW5pdGlvbiA9PiB7XG4gICAgICBjb25zdCB7IHZlcnNpb246IG1ldGhvZFZlcnNpb24gfSA9IHJvdXRlRGVmaW5pdGlvblxuICAgICAgcm91dGVQYXRoTWV0YWRhdGEubWV0aG9kVmVyc2lvbiA9IG1ldGhvZFZlcnNpb25cblxuICAgICAgdGhpcy5hcHBseUNhbGxiYWNrVG9Sb3V0ZXIoXG4gICAgICAgIHJvdXRlcixcbiAgICAgICAgcm91dGVEZWZpbml0aW9uLFxuICAgICAgICBpbnN0YW5jZVdyYXBwZXIsXG4gICAgICAgIG1vZHVsZUtleSxcbiAgICAgICAgcm91dGVQYXRoTWV0YWRhdGEsXG4gICAgICAgIGhvc3RcbiAgICAgIClcbiAgICB9KVxuICB9XG5cbiAgcHJpdmF0ZSBhcHBseUNhbGxiYWNrVG9Sb3V0ZXI8VCBleHRlbmRzIEh0dHBTZXJ2ZXI+IChcbiAgICByb3V0ZXI6IFQsXG4gICAgcm91dGVEZWZpbml0aW9uOiBSb3V0ZURlZmluaXRpb25JbnRlcmZhY2UsXG4gICAgaW5zdGFuY2VXcmFwcGVyOiBJbnN0YW5jZVdyYXBwZXIsXG4gICAgbW9kdWxlS2V5OiBzdHJpbmcsXG4gICAgcm91dGVQYXRoTWV0YWRhdGE6IFJvdXRlUGF0aE1ldGFkYXRhSW50ZXJmYWNlLFxuICAgIGhvc3Q6IHN0cmluZyB8IFJlZ0V4cCB8IEFycmF5PHN0cmluZyB8IFJlZ0V4cD4gfCB1bmRlZmluZWRcbiAgKSB7XG4gICAgY29uc3Qge1xuICAgICAgcGF0aDogcGF0aHMsXG4gICAgICByZXF1ZXN0TWV0aG9kLFxuICAgICAgdGFyZ2V0Q2FsbGJhY2ssXG4gICAgICBtZXRob2ROYW1lXG4gICAgfSA9IHJvdXRlRGVmaW5pdGlvblxuXG4gICAgY29uc3QgeyBpbnN0YW5jZSB9ID0gaW5zdGFuY2VXcmFwcGVyXG4gICAgY29uc3Qgcm91dGVyTWV0aG9kUmVmID0gdGhpcy5yb3V0ZXJNZXRob2RGYWN0b3J5XG4gICAgICAuZ2V0KHJvdXRlciwgcmVxdWVzdE1ldGhvZClcbiAgICAgIC5iaW5kKHJvdXRlcilcblxuICAgIGNvbnN0IGlzUmVxdWVzdFNjb3BlZCA9ICFpbnN0YW5jZVdyYXBwZXIuaXNEZXBlbmRlbmN5VHJlZVN0YXRpYygpXG4gICAgY29uc3QgcHJveHkgPSBpc1JlcXVlc3RTY29wZWRcbiAgICAgID8gdGhpcy5jcmVhdGVSZXF1ZXN0U2NvcGVkSGFuZGxlcihcbiAgICAgICAgICBpbnN0YW5jZVdyYXBwZXIsXG4gICAgICAgICAgcmVxdWVzdE1ldGhvZCxcbiAgICAgICAgICB0aGlzLmNvbnRhaW5lci5nZXRNb2R1bGVCeUtleShtb2R1bGVLZXkpLFxuICAgICAgICAgIG1vZHVsZUtleSxcbiAgICAgICAgICBtZXRob2ROYW1lXG4gICAgICAgIClcbiAgICAgIDogdGhpcy5jcmVhdGVDYWxsYmFja1Byb3h5KFxuICAgICAgICAgIGluc3RhbmNlLFxuICAgICAgICAgIHRhcmdldENhbGxiYWNrLFxuICAgICAgICAgIG1ldGhvZE5hbWUsXG4gICAgICAgICAgbW9kdWxlS2V5LFxuICAgICAgICAgIHJlcXVlc3RNZXRob2RcbiAgICAgICAgKVxuXG4gICAgbGV0IHJvdXRlSGFuZGxlciA9IHRoaXMuYXBwbHlIb3N0RmlsdGVyKGhvc3QsIHByb3h5KVxuXG4gICAgcGF0aHMuZm9yRWFjaChwYXRoID0+IHtcbiAgICAgIHJvdXRlUGF0aE1ldGFkYXRhLm1ldGhvZFBhdGggPSBwYXRoXG4gICAgICBjb25zdCBwYXRoc1RvUmVnaXN0ZXIgPSB0aGlzLnJvdXRlUGF0aEZhY3RvcnkuY3JlYXRlKFxuICAgICAgICByb3V0ZVBhdGhNZXRhZGF0YSxcbiAgICAgICAgcmVxdWVzdE1ldGhvZFxuICAgICAgKVxuICAgICAgcGF0aHNUb1JlZ2lzdGVyLmZvckVhY2gocGF0aCA9PiB7XG4gICAgICAgIGNvbnN0IGVudHJ5cG9pbnREZWZpbml0aW9uOiBFbnRyeXBvaW50PEh0dHBFbnRyeXBvaW50TWV0YWRhdGE+ID0ge1xuICAgICAgICAgIHR5cGU6ICdodHRwLWVuZHBvaW50JyxcbiAgICAgICAgICBtZXRob2ROYW1lLFxuICAgICAgICAgIGNsYXNzTmFtZTogaW5zdGFuY2VXcmFwcGVyLm5hbWUsXG4gICAgICAgICAgY2xhc3NOb2RlSWQ6IGluc3RhbmNlV3JhcHBlci5pZCxcbiAgICAgICAgICBtZXRhZGF0YToge1xuICAgICAgICAgICAga2V5OiBwYXRoLFxuICAgICAgICAgICAgcGF0aCxcbiAgICAgICAgICAgIHJlcXVlc3RNZXRob2Q6IFJlcXVlc3RNZXRob2RbXG4gICAgICAgICAgICAgIHJlcXVlc3RNZXRob2RcbiAgICAgICAgICAgIF0gYXMga2V5b2YgdHlwZW9mIFJlcXVlc3RNZXRob2RcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmNvcHlNZXRhZGF0YVRvQ2FsbGJhY2sodGFyZ2V0Q2FsbGJhY2ssIHJvdXRlSGFuZGxlcilcbiAgICAgICAgcm91dGVyTWV0aG9kUmVmKHBhdGgsIHJvdXRlSGFuZGxlcilcblxuICAgICAgICB0aGlzLmdyYXBoSW5zcGVjdG9yLmluc2VydEVudHJ5cG9pbnREZWZpbml0aW9uPEh0dHBFbnRyeXBvaW50TWV0YWRhdGE+KFxuICAgICAgICAgIGVudHJ5cG9pbnREZWZpbml0aW9uLFxuICAgICAgICAgIGluc3RhbmNlV3JhcHBlci5pZFxuICAgICAgICApXG4gICAgICB9KVxuXG4gICAgICBjb25zdCBwYXRoc1RvTG9nID0gdGhpcy5yb3V0ZVBhdGhGYWN0b3J5LmNyZWF0ZShcbiAgICAgICAge1xuICAgICAgICAgIC4uLnJvdXRlUGF0aE1ldGFkYXRhLFxuICAgICAgICAgIHZlcnNpb25pbmdPcHRpb25zOiB1bmRlZmluZWRcbiAgICAgICAgfSxcbiAgICAgICAgcmVxdWVzdE1ldGhvZFxuICAgICAgKVxuICAgICAgcGF0aHNUb0xvZy5mb3JFYWNoKHBhdGggPT5cbiAgICAgICAgdGhpcy5sb2dnZXIubG9nKFJPVVRFX01BUFBFRF9NRVNTQUdFKHBhdGgsIHJlcXVlc3RNZXRob2QpKVxuICAgICAgKVxuICAgIH0pXG4gIH1cblxuICBwcml2YXRlIGFwcGx5SG9zdEZpbHRlciAoXG4gICAgaG9zdDogc3RyaW5nIHwgUmVnRXhwIHwgQXJyYXk8c3RyaW5nIHwgUmVnRXhwPiB8IHVuZGVmaW5lZCxcbiAgICBoYW5kbGVyOiBGdW5jdGlvblxuICApIHtcbiAgICBpZiAoIWhvc3QpIHJldHVybiBoYW5kbGVyXG5cbiAgICBjb25zdCBodHRwQWRhcHRlclJlZiA9IHRoaXMuY29udGFpbmVyLmdldEh0dHBBZGFwdGVyUmVmKClcbiAgICBjb25zdCBob3N0cyA9IEFycmF5LmlzQXJyYXkoaG9zdCkgPyBob3N0IDogW2hvc3RdXG4gICAgY29uc3QgaG9zdFJlZ0V4cHMgPSBob3N0cy5tYXAoKGhvc3Q6IHN0cmluZyB8IFJlZ0V4cCkgPT4ge1xuICAgICAgY29uc3Qga2V5czogYW55ID0gW11cbiAgICAgIGNvbnN0IHJlZ2V4cCA9IHBhdGhUb1JlZ2V4cChob3N0LCBrZXlzKVxuICAgICAgcmV0dXJuIHsgcmVnZXhwLCBrZXlzIH1cbiAgICB9KVxuXG4gICAgY29uc3QgdW5zdXBwb3J0ZWRGaWx0ZXJpbmdFcnJvck1lc3NhZ2UgPSBBcnJheS5pc0FycmF5KGhvc3QpXG4gICAgICA/IGBIVFRQIGFkYXB0ZXIgZG9lcyBub3Qgc3VwcG9ydCBmaWx0ZXJpbmcgb24gaG9zdHM6IFtcIiR7aG9zdC5qb2luKFxuICAgICAgICAgICdcIiwgXCInXG4gICAgICAgICl9XCJdYFxuICAgICAgOiBgSFRUUCBhZGFwdGVyIGRvZXMgbm90IHN1cHBvcnQgZmlsdGVyaW5nIG9uIGhvc3Q6IFwiJHtob3N0fVwiYFxuXG4gICAgcmV0dXJuIDxUIGV4dGVuZHMgUmVjb3JkPHN0cmluZywgYW55PiA9IGFueSwgUiA9IGFueT4oXG4gICAgICByZXE6IFQsXG4gICAgICByZXM6IFIsXG4gICAgICBuZXh0OiAoKSA9PiB2b2lkXG4gICAgKSA9PiB7XG4gICAgICA7KHJlcSBhcyBSZWNvcmQ8c3RyaW5nLCBhbnk+KS5ob3N0cyA9IHt9XG4gICAgICBjb25zdCBob3N0bmFtZSA9IGh0dHBBZGFwdGVyUmVmLmdldFJlcXVlc3RIb3N0bmFtZShyZXEpIHx8ICcnXG5cbiAgICAgIGZvciAoY29uc3QgZXhwIG9mIGhvc3RSZWdFeHBzKSB7XG4gICAgICAgIGNvbnN0IG1hdGNoID0gaG9zdG5hbWUubWF0Y2goZXhwLnJlZ2V4cClcbiAgICAgICAgaWYgKG1hdGNoKSB7XG4gICAgICAgICAgZXhwLmtleXMuZm9yRWFjaChcbiAgICAgICAgICAgIChrZXk6IGFueSwgaTogYW55KSA9PiAocmVxLmhvc3RzW2tleS5uYW1lXSA9IG1hdGNoW2kgKyAxXSlcbiAgICAgICAgICApXG4gICAgICAgICAgcmV0dXJuIGhhbmRsZXIocmVxLCByZXMsIG5leHQpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmICghbmV4dClcbiAgICAgICAgdGhyb3cgbmV3IEludGVybmFsU2VydmVyRXJyb3JFeGNlcHRpb24odW5zdXBwb3J0ZWRGaWx0ZXJpbmdFcnJvck1lc3NhZ2UpXG4gICAgICByZXR1cm4gbmV4dCgpXG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBjcmVhdGVDYWxsYmFja1Byb3h5IChcbiAgICBpbnN0YW5jZTogQ29udHJvbGxlclR5cGUsXG4gICAgY2FsbGJhY2s6IFJvdXRlclByb3h5Q2FsbGJhY2ssXG4gICAgbWV0aG9kTmFtZTogc3RyaW5nLFxuICAgIG1vZHVsZVJlZjogc3RyaW5nLFxuICAgIHJlcXVlc3RNZXRob2Q6IFJlcXVlc3RNZXRob2QsXG4gICAgY29udGV4dElkID0gU1RBVElDX0NPTlRFWFQsXG4gICAgaW5xdWlyZXJJZD86IHN0cmluZ1xuICApIHtcbiAgICBjb25zdCBleGVjdXRpb25Db250ZXh0ID0gdGhpcy5leGVjdXRpb25Db250ZXh0Q3JlYXRvci5jcmVhdGUoXG4gICAgICBpbnN0YW5jZSxcbiAgICAgIGNhbGxiYWNrLFxuICAgICAgbWV0aG9kTmFtZSxcbiAgICAgIG1vZHVsZVJlZixcbiAgICAgIHJlcXVlc3RNZXRob2QsXG4gICAgICBjb250ZXh0SWQsXG4gICAgICBpbnF1aXJlcklkXG4gICAgKVxuICAgIGNvbnN0IGV4Y2VwdGlvbkZpbHRlciA9IHRoaXMuZXhjZXB0aW9uc0ZpbHRlci5jcmVhdGUoXG4gICAgICBpbnN0YW5jZSxcbiAgICAgIGNhbGxiYWNrLFxuICAgICAgbW9kdWxlUmVmLFxuICAgICAgY29udGV4dElkLFxuICAgICAgaW5xdWlyZXJJZFxuICAgIClcbiAgICByZXR1cm4gdGhpcy5yb3V0ZXJQcm94eT8uY3JlYXRlUHJveHkoZXhlY3V0aW9uQ29udGV4dCwgZXhjZXB0aW9uRmlsdGVyKVxuICB9XG5cbiAgcHVibGljIGNyZWF0ZVJlcXVlc3RTY29wZWRIYW5kbGVyIChcbiAgICBpbnN0YW5jZVdyYXBwZXI6IEluc3RhbmNlV3JhcHBlcixcbiAgICByZXF1ZXN0TWV0aG9kOiBSZXF1ZXN0TWV0aG9kLFxuICAgIG1vZHVsZVJlZjogTW9kdWxlLFxuICAgIG1vZHVsZUtleTogc3RyaW5nLFxuICAgIG1ldGhvZE5hbWU6IHN0cmluZ1xuICApIHtcbiAgICBjb25zdCB7IGluc3RhbmNlIH0gPSBpbnN0YW5jZVdyYXBwZXJcbiAgICBjb25zdCBjb2xsZWN0aW9uID0gbW9kdWxlUmVmLmNvbnRyb2xsZXJzXG4gICAgcmV0dXJuIGFzeW5jIDxUIGV4dGVuZHMgUmVjb3JkPGFueSwgYW55PiwgUj4oXG4gICAgICByZXE6IFQsXG4gICAgICByZXM6IFIsXG4gICAgICBuZXh0OiAoKSA9PiB2b2lkXG4gICAgKSA9PiB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBjb250ZXh0SWQgPSB0aGlzLmdldENvbnRleHRJZChyZXEpXG4gICAgICAgIGNvbnN0IGNvbnRleHRJbnN0YW5jZSA9IGF3YWl0IHRoaXMuaW5qZWN0b3I/LmxvYWRQZXJDb250ZXh0KFxuICAgICAgICAgIGluc3RhbmNlLFxuICAgICAgICAgIG1vZHVsZVJlZixcbiAgICAgICAgICBjb2xsZWN0aW9uLFxuICAgICAgICAgIGNvbnRleHRJZFxuICAgICAgICApXG4gICAgICAgIGF3YWl0IHRoaXMuY3JlYXRlQ2FsbGJhY2tQcm94eShcbiAgICAgICAgICBjb250ZXh0SW5zdGFuY2UsXG4gICAgICAgICAgY29udGV4dEluc3RhbmNlW21ldGhvZE5hbWVdLFxuICAgICAgICAgIG1ldGhvZE5hbWUsXG4gICAgICAgICAgbW9kdWxlS2V5LFxuICAgICAgICAgIHJlcXVlc3RNZXRob2QsXG4gICAgICAgICAgY29udGV4dElkLFxuICAgICAgICAgIGluc3RhbmNlV3JhcHBlci5pZFxuICAgICAgICApKHJlcSwgcmVzLCBuZXh0KVxuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGxldCBleGNlcHRpb25GaWx0ZXIgPSB0aGlzLmV4Y2VwdGlvbkZpbHRlcnNDYWNoZS5nZXQoXG4gICAgICAgICAgaW5zdGFuY2VbbWV0aG9kTmFtZV1cbiAgICAgICAgKVxuICAgICAgICBpZiAoIWV4Y2VwdGlvbkZpbHRlcikge1xuICAgICAgICAgIGV4Y2VwdGlvbkZpbHRlciA9IHRoaXMuZXhjZXB0aW9uc0ZpbHRlcj8uY3JlYXRlKFxuICAgICAgICAgICAgaW5zdGFuY2UsXG4gICAgICAgICAgICBpbnN0YW5jZVttZXRob2ROYW1lXSxcbiAgICAgICAgICAgIG1vZHVsZUtleVxuICAgICAgICAgIClcbiAgICAgICAgICB0aGlzLmV4Y2VwdGlvbkZpbHRlcnNDYWNoZS5zZXQoaW5zdGFuY2VbbWV0aG9kTmFtZV0sIGV4Y2VwdGlvbkZpbHRlcilcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBob3N0ID0gbmV3IEV4ZWN1dGlvbkNvbnRleHRIb3N0KFtyZXEsIHJlcywgbmV4dF0pXG4gICAgICAgIGV4Y2VwdGlvbkZpbHRlci5uZXh0KGVyciwgaG9zdClcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGdldENvbnRleHRJZDxUIGV4dGVuZHMgUmVjb3JkPGFueSwgdW5rbm93bj4gPSBhbnk+IChcbiAgICByZXF1ZXN0OiBUXG4gICk6IENvbnRleHRJZCB7XG4gICAgY29uc3QgY29udGV4dElkID0gQ29udGV4dElkRmFjdG9yeS5nZXRCeVJlcXVlc3QocmVxdWVzdClcbiAgICBpZiAoIXJlcXVlc3RbUkVRVUVTVF9DT05URVhUX0lEIGFzIGFueV0pIHtcbiAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShyZXF1ZXN0LCBSRVFVRVNUX0NPTlRFWFRfSUQsIHtcbiAgICAgICAgdmFsdWU6IGNvbnRleHRJZCxcbiAgICAgICAgZW51bWVyYWJsZTogZmFsc2UsXG4gICAgICAgIHdyaXRhYmxlOiBmYWxzZSxcbiAgICAgICAgY29uZmlndXJhYmxlOiBmYWxzZVxuICAgICAgfSlcbiAgICAgIHRoaXMuY29udGFpbmVyLnJlZ2lzdGVyUmVxdWVzdFByb3ZpZGVyKHJlcXVlc3QsIGNvbnRleHRJZClcbiAgICB9XG4gICAgcmV0dXJuIGNvbnRleHRJZFxuICB9XG5cbiAgcHJpdmF0ZSBjb3B5TWV0YWRhdGFUb0NhbGxiYWNrIChcbiAgICBvcmlnaW5hbENhbGxiYWNrOiBSb3V0ZXJQcm94eUNhbGxiYWNrLFxuICAgIHRhcmdldENhbGxiYWNrOiBGdW5jdGlvblxuICApIHtcbiAgICBmb3IgKGNvbnN0IGtleSBvZiBSZWZsZWN0LmdldE1ldGFkYXRhS2V5cyhvcmlnaW5hbENhbGxiYWNrKSkge1xuICAgICAgUmVmbGVjdC5kZWZpbmVNZXRhZGF0YShcbiAgICAgICAga2V5LFxuICAgICAgICBSZWZsZWN0LmdldE1ldGFkYXRhKGtleSwgb3JpZ2luYWxDYWxsYmFjayksXG4gICAgICAgIHRhcmdldENhbGxiYWNrXG4gICAgICApXG4gICAgfVxuICB9XG59XG4iXX0=