UNPKG

@jufab/opentelemetry-angular-interceptor

Version:

@jufab/opentelemetry-angular-interceptor is an Angular Library to deploy [OpenTelemetry](https://opentelemetry.io/) in your Angular application

258 lines 34.1 kB
import { Injectable, Inject, Optional } from '@angular/core'; import { PlatformLocation } from '@angular/common'; import * as api from '@opentelemetry/api'; import { SpanStatusCode, SpanKind } from '@opentelemetry/api'; import { WebTracerProvider, StackContextManager } from '@opentelemetry/sdk-trace-web'; import { SimpleSpanProcessor, ConsoleSpanExporter, BatchSpanProcessor, NoopSpanProcessor, AlwaysOnSampler, AlwaysOffSampler, TraceIdRatioBasedSampler, ParentBasedSampler } from '@opentelemetry/sdk-trace-base'; import { isUrlIgnored } from '@opentelemetry/core'; import { ATTR_USER_AGENT_ORIGINAL, ATTR_URL_QUERY, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_ERROR_TYPE, ATTR_SERVICE_NAME, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL, ATTR_URL_SCHEME, ATTR_SERVER_ADDRESS, ATTR_SERVER_PORT, // ATTR_HTTP_REQUEST_HEADER, //SEMATTRS_ERROR_TYPE } from '@opentelemetry/semantic-conventions'; import { resourceFromAttributes } from '@opentelemetry/resources'; import { tap, finalize } from 'rxjs/operators'; import { OTEL_CONFIG, } from '../configuration/opentelemetry-config'; import infoLibrary from '../../version.json'; import { OTEL_EXPORTER } from '../services/exporter/exporter.interface'; import { OTEL_PROPAGATOR } from '../services/propagator/propagator.interface'; import { OTEL_LOGGER, OTEL_CUSTOM_SPAN } from '../configuration/opentelemetry-config'; import * as i0 from "@angular/core"; import * as i1 from "@angular/common"; /** * OpenTelemetryInterceptor class */ export class OpenTelemetryHttpInterceptor { /** * constructor * * @param config configuration * @param exporterService service exporter injected * @param propagatorService propagator injected * @param logger define logger * @param customSpan a customSpan interface to add attributes * @param platformLocation encapsulates all calls to DOM APIs */ constructor(config, exporterService, propagatorService, logger, customSpan, platformLocation) { this.config = config; this.exporterService = exporterService; this.propagatorService = propagatorService; this.logger = logger; this.customSpan = customSpan; this.platformLocation = platformLocation; /** * Log or not body */ this.logBody = false; this.tracer = new WebTracerProvider({ sampler: this.defineProbabilitySampler(this.convertStringToNumber(config.commonConfig.probabilitySampler)), resource: this.loadResourceAttributes(this.config.commonConfig), spanProcessors: this.insertOrNotSpanExporter() }); this.contextManager = new StackContextManager(); this.tracer.register({ propagator: this.propagatorService.getPropagator(), contextManager: this.contextManager }); this.logBody = config.commonConfig.logBody; api.diag.setLogger(logger, config.commonConfig.logLevel); } /** * Overide method * Interceptor from HttpInterceptor Angular * * @param request the current request * @param next next */ intercept(request, next) { if (isUrlIgnored(request.url, this.config.ignoreUrls?.urls)) { return next.handle(request); } this.contextManager.disable(); //FIX - reinit contextManager for each http call this.contextManager.enable(); const span = this.initSpan(request); const tracedReq = this.injectContextAndHeader(request); return next.handle(tracedReq).pipe(tap((event) => { span.setAttributes({ [ATTR_HTTP_RESPONSE_STATUS_CODE]: event.status, }); if (this.logBody && event.body != null) { span.addEvent('response', { body: JSON.stringify(event.body) }); } span.setStatus({ code: SpanStatusCode.UNSET }); this.setCustomSpan(span, request, event); }, (event) => { span.setAttributes({ [ATTR_HTTP_RESPONSE_STATUS_CODE]: event.status, [ATTR_ERROR_TYPE]: event.name, }); span.recordException({ name: event.name, message: event.message, stack: event.error }); span.setStatus({ code: SpanStatusCode.ERROR }); this.setCustomSpan(span, request, event); }), finalize(() => { span.end(); this.contextManager.disable(); })); } /** * Get current scheme, hostname and port */ getURL() { return this.platformLocation.href; } /** * Generate Resource Attributes */ loadResourceAttributes(commonConfig) { return resourceFromAttributes({ [ATTR_SERVICE_NAME]: commonConfig?.serviceName, ...commonConfig?.resourceAttributes, }); } /** * Initialise a span for a request intercepted * * @param request request */ initSpan(request) { const urlRequest = (request.urlWithParams.startsWith('http')) ? new URL(request.urlWithParams) : new URL(this.getURL()); const span = this.tracer .getTracer(infoLibrary.name, infoLibrary.version) .startSpan(`${request.method.toUpperCase()}`, { attributes: { [ATTR_HTTP_REQUEST_METHOD]: request.method, [ATTR_SERVER_ADDRESS]: urlRequest.host, [ATTR_SERVER_PORT]: urlRequest.port, [ATTR_URL_FULL]: request.urlWithParams, [ATTR_URL_SCHEME]: urlRequest.protocol.replace(':', ''), [ATTR_URL_QUERY]: urlRequest.search, [ATTR_USER_AGENT_ORIGINAL]: window.navigator.userAgent }, kind: SpanKind.CLIENT, }, this.contextManager.active()); /*eslint no-underscore-dangle: ["error", { "allow": ["_currentContext"] }]*/ this.contextManager._currentContext = api.trace.setSpan(this.contextManager.active(), span); return span; } /** * Add header propagator in request and conserve original header * * @param request request */ injectContextAndHeader(request) { const carrier = {}; api.propagation.inject(this.contextManager.active(), carrier, api.defaultTextMapSetter); request.headers.keys().map(key => { carrier[key] = request.headers.get(key); }); return request.clone({ setHeaders: carrier, }); } /** * Verify to insert or not a Span Exporter */ insertOrNotSpanExporter() { if (this.exporterService.getExporter() !== undefined) { return Array.of(this.insertSpanProcessorProductionMode(), this.insertConsoleSpanExporter()); } else { return Array.of(new NoopSpanProcessor()); } } /** * Insert in tracer the console span if config is true */ insertConsoleSpanExporter() { if (this.config.commonConfig.console) { return new SimpleSpanProcessor(new ConsoleSpanExporter()); } } /** * Insert BatchSpanProcessor in production mode * SimpleSpanProcessor otherwise */ insertSpanProcessorProductionMode() { const bufferConfig = { maxExportBatchSize: this.convertStringToNumber(this.config.batchSpanProcessorConfig?.maxExportBatchSize), scheduledDelayMillis: this.convertStringToNumber(this.config.batchSpanProcessorConfig?.scheduledDelayMillis), exportTimeoutMillis: this.convertStringToNumber(this.config.batchSpanProcessorConfig?.exportTimeoutMillis), maxQueueSize: this.convertStringToNumber(this.config.batchSpanProcessorConfig?.maxQueueSize) }; return this.config.commonConfig.production ? new BatchSpanProcessor(this.exporterService.getExporter(), bufferConfig) : new SimpleSpanProcessor(this.exporterService.getExporter()); } /** * define the Probability Sampler * By Default, it's always (or 1) * * @param sampleConfig the sample configuration */ defineProbabilitySampler(sampleConfig) { if (sampleConfig >= 1) { return new ParentBasedSampler({ root: new AlwaysOnSampler() }); } else if (sampleConfig <= 0 || sampleConfig === undefined) { return new ParentBasedSampler({ root: new AlwaysOffSampler() }); } else { return new ParentBasedSampler({ root: new TraceIdRatioBasedSampler(sampleConfig) }); } } /** * convert String to Number (or undefined) * * @param value * @returns number or undefined */ convertStringToNumber(value) { return value !== undefined ? Number(value) : undefined; } /** * Set custom attributes in span with a CustomSpan * * @param span * @param request * @param response * @returns Span */ setCustomSpan(span, request, response) { return this.customSpan != null ? this.customSpan.add(span, request, response) : span; } } OpenTelemetryHttpInterceptor.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: OpenTelemetryHttpInterceptor, deps: [{ token: OTEL_CONFIG }, { token: OTEL_EXPORTER }, { token: OTEL_PROPAGATOR }, { token: OTEL_LOGGER, optional: true }, { token: OTEL_CUSTOM_SPAN, optional: true }, { token: i1.PlatformLocation }], target: i0.ɵɵFactoryTarget.Injectable }); OpenTelemetryHttpInterceptor.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: OpenTelemetryHttpInterceptor, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: OpenTelemetryHttpInterceptor, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Inject, args: [OTEL_CONFIG] }] }, { type: undefined, decorators: [{ type: Inject, args: [OTEL_EXPORTER] }] }, { type: undefined, decorators: [{ type: Inject, args: [OTEL_PROPAGATOR] }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [OTEL_LOGGER] }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [OTEL_CUSTOM_SPAN] }] }, { type: i1.PlatformLocation }]; } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BlbnRlbGVtZXRyeS1odHRwLmludGVyY2VwdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvb3BlbnRlbGVtZXRyeS1pbnRlcmNlcHRvci9zcmMvbGliL2ludGVyY2VwdG9yL29wZW50ZWxlbWV0cnktaHR0cC5pbnRlcmNlcHRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFTN0QsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFbkQsT0FBTyxLQUFLLEdBQUcsTUFBTSxvQkFBb0IsQ0FBQztBQUMxQyxPQUFPLEVBQVEsY0FBYyxFQUFjLFFBQVEsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ2hGLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQ3RGLE9BQU8sRUFDTCxtQkFBbUIsRUFDbkIsbUJBQW1CLEVBQ25CLGtCQUFrQixFQUNsQixpQkFBaUIsRUFDakIsZUFBZSxFQUNmLGdCQUFnQixFQUNoQix3QkFBd0IsRUFDeEIsa0JBQWtCLEVBR25CLE1BQU0sK0JBQStCLENBQUM7QUFDdkMsT0FBTyxFQUNMLFlBQVksRUFDYixNQUFNLHFCQUFxQixDQUFDO0FBQzdCLE9BQU8sRUFDTCx3QkFBd0IsRUFFeEIsY0FBYyxFQUNkLDhCQUE4QixFQUM5QixlQUFlLEVBQ2YsaUJBQWlCLEVBQ2pCLHdCQUF3QixFQUN4QixhQUFhLEVBQ2IsZUFBZSxFQUNmLG1CQUFtQixFQUNuQixnQkFBZ0I7QUFDaEIsNEJBQTRCO0FBQzVCLHFCQUFxQjtFQUN0QixNQUFNLHFDQUFxQyxDQUFDO0FBQzdDLE9BQU8sRUFBWSxzQkFBc0IsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQzVFLE9BQU8sRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDL0MsT0FBTyxFQUdMLFdBQVcsR0FDWixNQUFNLHVDQUF1QyxDQUFDO0FBQy9DLE9BQU8sV0FBVyxNQUFNLG9CQUFvQixDQUFDO0FBQzdDLE9BQU8sRUFBRSxhQUFhLEVBQWEsTUFBTSx5Q0FBeUMsQ0FBQztBQUNuRixPQUFPLEVBQUUsZUFBZSxFQUFlLE1BQU0sNkNBQTZDLENBQUM7QUFDM0YsT0FBTyxFQUFFLFdBQVcsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHVDQUF1QyxDQUFDOzs7QUFHdEY7O0dBRUc7QUFJSCxNQUFNLE9BQU8sNEJBQTRCO0lBY3RDOzs7Ozs7Ozs7TUFTRTtJQUNGLFlBQzhCLE1BQTJCLEVBRWhELGVBQTBCLEVBRTFCLGlCQUE4QixFQUU5QixNQUFrQixFQUVsQixVQUFzQixFQUN0QixnQkFBa0M7UUFUYixXQUFNLEdBQU4sTUFBTSxDQUFxQjtRQUVoRCxvQkFBZSxHQUFmLGVBQWUsQ0FBVztRQUUxQixzQkFBaUIsR0FBakIsaUJBQWlCLENBQWE7UUFFOUIsV0FBTSxHQUFOLE1BQU0sQ0FBWTtRQUVsQixlQUFVLEdBQVYsVUFBVSxDQUFZO1FBQ3RCLHFCQUFnQixHQUFoQixnQkFBZ0IsQ0FBa0I7UUF6QjVDOztXQUVHO1FBQ0gsWUFBTyxHQUFHLEtBQUssQ0FBQztRQXdCZCxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksaUJBQWlCLENBQUM7WUFDbEMsT0FBTyxFQUFFLElBQUksQ0FBQyx3QkFBd0IsQ0FBQyxJQUFJLENBQUMscUJBQXFCLENBQUMsTUFBTSxDQUFDLFlBQVksQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO1lBQzFHLFFBQVEsRUFBRSxJQUFJLENBQUMsc0JBQXNCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUM7WUFDL0QsY0FBYyxFQUFFLElBQUksQ0FBQyx1QkFBdUIsRUFBRTtTQUMvQyxDQUFDLENBQUM7UUFDSCxJQUFJLENBQUMsY0FBYyxHQUFHLElBQUksbUJBQW1CLEVBQUUsQ0FBQztRQUNoRCxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQztZQUNuQixVQUFVLEVBQUUsSUFBSSxDQUFDLGlCQUFpQixDQUFDLGFBQWEsRUFBRTtZQUNsRCxjQUFjLEVBQUUsSUFBSSxDQUFDLGNBQWM7U0FDcEMsQ0FBQyxDQUFDO1FBQ0gsSUFBSSxDQUFDLE9BQU8sR0FBRyxNQUFNLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQztRQUMzQyxHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLFlBQVksQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUMzRCxDQUFDO0lBRUM7Ozs7OztLQU1DO0lBQ0QsU0FBUyxDQUNQLE9BQTZCLEVBQzdCLElBQWlCO1FBRWpCLElBQUksWUFBWSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxVQUFVLEVBQUUsSUFBSSxDQUFDLEVBQUU7WUFDM0QsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1NBQzdCO1FBQ0QsSUFBSSxDQUFDLGNBQWMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLGdEQUFnRDtRQUMvRSxJQUFJLENBQUMsY0FBYyxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQzdCLE1BQU0sSUFBSSxHQUFTLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDMUMsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLHNCQUFzQixDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ3ZELE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQyxJQUFJLENBQ2hDLEdBQUcsQ0FDRCxDQUFDLEtBQXdCLEVBQUUsRUFBRTtZQUMzQixJQUFJLENBQUMsYUFBYSxDQUNoQjtnQkFDRSxDQUFDLDhCQUE4QixDQUFDLEVBQUUsS0FBSyxDQUFDLE1BQU07YUFDL0MsQ0FDRixDQUFDO1lBQ0YsSUFBSSxJQUFJLENBQUMsT0FBTyxJQUFJLEtBQUssQ0FBQyxJQUFJLElBQUksSUFBSSxFQUFFO2dCQUN0QyxJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7YUFDakU7WUFDRCxJQUFJLENBQUMsU0FBUyxDQUFDO2dCQUNiLElBQUksRUFBRSxjQUFjLENBQUMsS0FBSzthQUMzQixDQUFDLENBQUM7WUFDSCxJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksRUFBRSxPQUFPLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDM0MsQ0FBQyxFQUNELENBQUMsS0FBd0IsRUFBRSxFQUFFO1lBQzNCLElBQUksQ0FBQyxhQUFhLENBQ2hCO2dCQUNFLENBQUMsOEJBQThCLENBQUMsRUFBRSxLQUFLLENBQUMsTUFBTTtnQkFDOUMsQ0FBQyxlQUFlLENBQUMsRUFBRyxLQUFLLENBQUMsSUFBSTthQUMvQixDQUNGLENBQUM7WUFDRixJQUFJLENBQUMsZUFBZSxDQUFDO2dCQUNuQixJQUFJLEVBQUUsS0FBSyxDQUFDLElBQUk7Z0JBQ2hCLE9BQU8sRUFBRSxLQUFLLENBQUMsT0FBTztnQkFDdEIsS0FBSyxFQUFFLEtBQUssQ0FBQyxLQUFLO2FBQ25CLENBQUMsQ0FBQztZQUNILElBQUksQ0FBQyxTQUFTLENBQUM7Z0JBQ2IsSUFBSSxFQUFFLGNBQWMsQ0FBQyxLQUFLO2FBQzNCLENBQUMsQ0FBQztZQUNILElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQztRQUMzQyxDQUFDLENBQ0YsRUFDRCxRQUFRLENBQUMsR0FBRyxFQUFFO1lBQ1osSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ1gsSUFBSSxDQUFDLGNBQWMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNoQyxDQUFDLENBQUMsQ0FDSCxDQUFDO0lBQ0osQ0FBQztJQUVIOztPQUVHO0lBQ0ssTUFBTTtRQUNaLE9BQU8sSUFBSSxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQztJQUNwQyxDQUFDO0lBRUQ7O09BRUc7SUFDSyxzQkFBc0IsQ0FDNUIsWUFBbUM7UUFFbkMsT0FBTyxzQkFBc0IsQ0FBQztZQUM1QixDQUFDLGlCQUFpQixDQUFDLEVBQUUsWUFBWSxFQUFFLFdBQVc7WUFDOUMsR0FBRyxZQUFZLEVBQUUsa0JBQWtCO1NBQ3BDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFDRDs7OztPQUlHO0lBQ0ssUUFBUSxDQUFDLE9BQTZCO1FBQzVDLE1BQU0sVUFBVSxHQUFHLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxHQUFHLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUN4SCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsTUFBTTthQUNyQixTQUFTLENBQUMsV0FBVyxDQUFDLElBQUksRUFBRSxXQUFXLENBQUMsT0FBTyxDQUFDO2FBQ2hELFNBQVMsQ0FDUixHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsV0FBVyxFQUFFLEVBQUUsRUFDakM7WUFDRSxVQUFVLEVBQUU7Z0JBQ1YsQ0FBQyx3QkFBd0IsQ0FBQyxFQUFFLE9BQU8sQ0FBQyxNQUFNO2dCQUMxQyxDQUFDLG1CQUFtQixDQUFDLEVBQUUsVUFBVSxDQUFDLElBQUk7Z0JBQ3RDLENBQUMsZ0JBQWdCLENBQUMsRUFBRSxVQUFVLENBQUMsSUFBSTtnQkFDbkMsQ0FBQyxhQUFhLENBQUMsRUFBRSxPQUFPLENBQUMsYUFBYTtnQkFDdEMsQ0FBQyxlQUFlLENBQUMsRUFBRSxVQUFVLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDO2dCQUN2RCxDQUFDLGNBQWMsQ0FBQyxFQUFFLFVBQVUsQ0FBQyxNQUFNO2dCQUNuQyxDQUFDLHdCQUF3QixDQUFDLEVBQUUsTUFBTSxDQUFDLFNBQVMsQ0FBQyxTQUFTO2FBQ3ZEO1lBQ0QsSUFBSSxFQUFFLFFBQVEsQ0FBQyxNQUFNO1NBQ3RCLEVBQ0QsSUFBSSxDQUFDLGNBQWMsQ0FBQyxNQUFNLEVBQUUsQ0FDN0IsQ0FBQztRQUNKLDRFQUE0RTtRQUM1RSxJQUFJLENBQUMsY0FBYyxDQUFDLGVBQWUsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FDckQsSUFBSSxDQUFDLGNBQWMsQ0FBQyxNQUFNLEVBQUUsRUFDNUIsSUFBSSxDQUNMLENBQUM7UUFDRixPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ssc0JBQXNCLENBQzVCLE9BQTZCO1FBRTdCLE1BQU0sT0FBTyxHQUFHLEVBQUUsQ0FBQztRQUNuQixHQUFHLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FDcEIsSUFBSSxDQUFDLGNBQWMsQ0FBQyxNQUFNLEVBQUUsRUFDNUIsT0FBTyxFQUNQLEdBQUcsQ0FBQyxvQkFBb0IsQ0FDekIsQ0FBQztRQUNGLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxFQUFFO1lBQy9CLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUMxQyxDQUFDLENBQUMsQ0FBQztRQUNILE9BQU8sT0FBTyxDQUFDLEtBQUssQ0FBQztZQUNuQixVQUFVLEVBQUUsT0FBTztTQUNwQixDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQ7O09BRUc7SUFDSyx1QkFBdUI7UUFFN0IsSUFBSSxJQUFJLENBQUMsZUFBZSxDQUFDLFdBQVcsRUFBRSxLQUFLLFNBQVMsRUFBRTtZQUNwRCxPQUFPLEtBQUssQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLGlDQUFpQyxFQUFFLEVBQUUsSUFBSSxDQUFDLHlCQUF5QixFQUFFLENBQUMsQ0FBQztTQUM3RjthQUFNO1lBQ0wsT0FBTyxLQUFLLENBQUMsRUFBRSxDQUFDLElBQUksaUJBQWlCLEVBQUUsQ0FBQyxDQUFDO1NBQzFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0sseUJBQXlCO1FBQy9CLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsT0FBTyxFQUFFO1lBQ3BDLE9BQU8sSUFBSSxtQkFBbUIsQ0FBQyxJQUFJLG1CQUFtQixFQUFFLENBQUMsQ0FBQztTQUMzRDtJQUNILENBQUM7SUFFRDs7O09BR0c7SUFDSyxpQ0FBaUM7UUFDdkMsTUFBTSxZQUFZLEdBQWlCO1lBQ2pDLGtCQUFrQixFQUFFLElBQUksQ0FBQyxxQkFBcUIsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLHdCQUF3QixFQUFFLGtCQUFrQixDQUFDO1lBQ3hHLG9CQUFvQixFQUFFLElBQUksQ0FBQyxxQkFBcUIsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLHdCQUF3QixFQUFFLG9CQUFvQixDQUFDO1lBQzVHLG1CQUFtQixFQUFFLElBQUksQ0FBQyxxQkFBcUIsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLHdCQUF3QixFQUFFLG1CQUFtQixDQUFDO1lBQzFHLFlBQVksRUFBRSxJQUFJLENBQUMscUJBQXFCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyx3QkFBd0IsRUFBRSxZQUFZLENBQUM7U0FDN0YsQ0FBQztRQUNGLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsVUFBVTtZQUN0QyxDQUFDLENBQUMsSUFBSSxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLFdBQVcsRUFBRSxFQUFFLFlBQVksQ0FBQztZQUMxRSxDQUFDLENBQUMsSUFBSSxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUM7SUFDcEUsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0ssd0JBQXdCLENBQUMsWUFBb0I7UUFDbkQsSUFBSSxZQUFZLElBQUksQ0FBQyxFQUFFO1lBQ3JCLE9BQU8sSUFBSSxrQkFBa0IsQ0FBQyxFQUFFLElBQUksRUFBRSxJQUFJLGVBQWUsRUFBRSxFQUFFLENBQUMsQ0FBQztTQUNoRTthQUNJLElBQUksWUFBWSxJQUFJLENBQUMsSUFBSSxZQUFZLEtBQUssU0FBUyxFQUFFO1lBQ3hELE9BQU8sSUFBSSxrQkFBa0IsQ0FBQyxFQUFFLElBQUksRUFBRSxJQUFJLGdCQUFnQixFQUFFLEVBQUUsQ0FBQyxDQUFDO1NBQ2pFO2FBQU07WUFDTCxPQUFPLElBQUksa0JBQWtCLENBQUMsRUFBRSxJQUFJLEVBQUUsSUFBSSx3QkFBd0IsQ0FBQyxZQUFZLENBQUMsRUFBRSxDQUFDLENBQUM7U0FDckY7SUFDSCxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSyxxQkFBcUIsQ0FBQyxLQUFhO1FBQ3pDLE9BQU8sS0FBSyxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFDekQsQ0FBQztJQUVEOzs7Ozs7O09BT0c7SUFDSyxhQUFhLENBQUMsSUFBVSxFQUFFLE9BQTZCLEVBQUUsUUFBbUQ7UUFDbEgsT0FBTyxJQUFJLENBQUMsVUFBVSxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO0lBQ3ZGLENBQUM7O3lIQWhRVSw0QkFBNEIsa0JBeUI3QixXQUFXLGFBQ1gsYUFBYSxhQUViLGVBQWUsYUFFSCxXQUFXLDZCQUVYLGdCQUFnQjs2SEFoQzNCLDRCQUE0QixjQUYzQixNQUFNOzJGQUVQLDRCQUE0QjtrQkFIeEMsVUFBVTttQkFBQztvQkFDVixVQUFVLEVBQUUsTUFBTTtpQkFDbkI7OzBCQTBCSSxNQUFNOzJCQUFDLFdBQVc7OzBCQUNsQixNQUFNOzJCQUFDLGFBQWE7OzBCQUVwQixNQUFNOzJCQUFDLGVBQWU7OzBCQUV0QixRQUFROzswQkFBSSxNQUFNOzJCQUFDLFdBQVc7OzBCQUU5QixRQUFROzswQkFBSSxNQUFNOzJCQUFDLGdCQUFnQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUsIEluamVjdCwgT3B0aW9uYWwgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7XG4gIEh0dHBSZXF1ZXN0LFxuICBIdHRwSGFuZGxlcixcbiAgSHR0cEV2ZW50LFxuICBIdHRwSW50ZXJjZXB0b3IsXG4gIEh0dHBSZXNwb25zZSxcbiAgSHR0cEVycm9yUmVzcG9uc2Vcbn0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uL2h0dHAnO1xuaW1wb3J0IHsgUGxhdGZvcm1Mb2NhdGlvbiB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQgeyBPYnNlcnZhYmxlIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgKiBhcyBhcGkgZnJvbSAnQG9wZW50ZWxlbWV0cnkvYXBpJztcbmltcG9ydCB7IFNwYW4sIFNwYW5TdGF0dXNDb2RlLCBEaWFnTG9nZ2VyLCBTcGFuS2luZCB9IGZyb20gJ0BvcGVudGVsZW1ldHJ5L2FwaSc7XG5pbXBvcnQgeyBXZWJUcmFjZXJQcm92aWRlciwgU3RhY2tDb250ZXh0TWFuYWdlciB9IGZyb20gJ0BvcGVudGVsZW1ldHJ5L3Nkay10cmFjZS13ZWInO1xuaW1wb3J0IHtcbiAgU2ltcGxlU3BhblByb2Nlc3NvcixcbiAgQ29uc29sZVNwYW5FeHBvcnRlcixcbiAgQmF0Y2hTcGFuUHJvY2Vzc29yLFxuICBOb29wU3BhblByb2Nlc3NvcixcbiAgQWx3YXlzT25TYW1wbGVyLFxuICBBbHdheXNPZmZTYW1wbGVyLFxuICBUcmFjZUlkUmF0aW9CYXNlZFNhbXBsZXIsXG4gIFBhcmVudEJhc2VkU2FtcGxlcixcbiAgU2FtcGxlcixcbiAgQnVmZmVyQ29uZmlnXG59IGZyb20gJ0BvcGVudGVsZW1ldHJ5L3Nkay10cmFjZS1iYXNlJztcbmltcG9ydCB7XG4gIGlzVXJsSWdub3JlZFxufSBmcm9tICdAb3BlbnRlbGVtZXRyeS9jb3JlJztcbmltcG9ydCB7XG4gIEFUVFJfVVNFUl9BR0VOVF9PUklHSU5BTCxcbiAgQVRUUl9VUkxfUEFUSCxcbiAgQVRUUl9VUkxfUVVFUlksXG4gIEFUVFJfSFRUUF9SRVNQT05TRV9TVEFUVVNfQ09ERSxcbiAgQVRUUl9FUlJPUl9UWVBFLFxuICBBVFRSX1NFUlZJQ0VfTkFNRSxcbiAgQVRUUl9IVFRQX1JFUVVFU1RfTUVUSE9ELFxuICBBVFRSX1VSTF9GVUxMLFxuICBBVFRSX1VSTF9TQ0hFTUUsXG4gIEFUVFJfU0VSVkVSX0FERFJFU1MsXG4gIEFUVFJfU0VSVkVSX1BPUlQsXG4gIC8vIEFUVFJfSFRUUF9SRVFVRVNUX0hFQURFUixcbiAgLy9TRU1BVFRSU19FUlJPUl9UWVBFXG59IGZyb20gJ0BvcGVudGVsZW1ldHJ5L3NlbWFudGljLWNvbnZlbnRpb25zJztcbmltcG9ydCB7IFJlc291cmNlLCByZXNvdXJjZUZyb21BdHRyaWJ1dGVzIH0gZnJvbSAnQG9wZW50ZWxlbWV0cnkvcmVzb3VyY2VzJztcbmltcG9ydCB7IHRhcCwgZmluYWxpemUgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQge1xuICBDb21tb25Db2xsZWN0b3JDb25maWcsXG4gIE9wZW5UZWxlbWV0cnlDb25maWcsXG4gIE9URUxfQ09ORklHLFxufSBmcm9tICcuLi9jb25maWd1cmF0aW9uL29wZW50ZWxlbWV0cnktY29uZmlnJztcbmltcG9ydCBpbmZvTGlicmFyeSBmcm9tICcuLi8uLi92ZXJzaW9uLmpzb24nO1xuaW1wb3J0IHsgT1RFTF9FWFBPUlRFUiwgSUV4cG9ydGVyIH0gZnJvbSAnLi4vc2VydmljZXMvZXhwb3J0ZXIvZXhwb3J0ZXIuaW50ZXJmYWNlJztcbmltcG9ydCB7IE9URUxfUFJPUEFHQVRPUiwgSVByb3BhZ2F0b3IgfSBmcm9tICcuLi9zZXJ2aWNlcy9wcm9wYWdhdG9yL3Byb3BhZ2F0b3IuaW50ZXJmYWNlJztcbmltcG9ydCB7IE9URUxfTE9HR0VSLCBPVEVMX0NVU1RPTV9TUEFOIH0gZnJvbSAnLi4vY29uZmlndXJhdGlvbi9vcGVudGVsZW1ldHJ5LWNvbmZpZyc7XG5pbXBvcnQgeyBDdXN0b21TcGFuIH0gZnJvbSAnLi9jdXN0b20tc3Bhbi5pbnRlcmZhY2UnO1xuXG4vKipcbiAqIE9wZW5UZWxlbWV0cnlJbnRlcmNlcHRvciBjbGFzc1xuICovXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290Jyxcbn0pXG5leHBvcnQgY2xhc3MgT3BlblRlbGVtZXRyeUh0dHBJbnRlcmNlcHRvciBpbXBsZW1lbnRzIEh0dHBJbnRlcmNlcHRvciB7XG4gIC8qKlxuICAgKiB0cmFjZXJcbiAgICovXG4gIHRyYWNlcjogV2ViVHJhY2VyUHJvdmlkZXI7XG4gIC8qKlxuICAgKiBjb250ZXh0IG1hbmFnZXJcbiAgICovXG4gIGNvbnRleHRNYW5hZ2VyOiBTdGFja0NvbnRleHRNYW5hZ2VyO1xuICAvKipcbiAgICogTG9nIG9yIG5vdCBib2R5XG4gICAqL1xuICBsb2dCb2R5ID0gZmFsc2U7XG5cbiAgIC8qKlxuICAgKiBjb25zdHJ1Y3RvclxuICAgKlxuICAgKiBAcGFyYW0gY29uZmlnIGNvbmZpZ3VyYXRpb25cbiAgICogQHBhcmFtIGV4cG9ydGVyU2VydmljZSBzZXJ2aWNlIGV4cG9ydGVyIGluamVjdGVkXG4gICAqIEBwYXJhbSBwcm9wYWdhdG9yU2VydmljZSBwcm9wYWdhdG9yIGluamVjdGVkXG4gICAqIEBwYXJhbSBsb2dnZXIgZGVmaW5lIGxvZ2dlclxuICAgKiBAcGFyYW0gY3VzdG9tU3BhbiBhIGN1c3RvbVNwYW4gaW50ZXJmYWNlIHRvIGFkZCBhdHRyaWJ1dGVzXG4gICAqIEBwYXJhbSBwbGF0Zm9ybUxvY2F0aW9uIGVuY2Fwc3VsYXRlcyBhbGwgY2FsbHMgdG8gRE9NIEFQSXNcbiAgICovXG4gICBjb25zdHJ1Y3RvcihcbiAgICBASW5qZWN0KE9URUxfQ09ORklHKSBwcml2YXRlIGNvbmZpZzogT3BlblRlbGVtZXRyeUNvbmZpZyxcbiAgICBASW5qZWN0KE9URUxfRVhQT1JURVIpXG4gICAgcHJpdmF0ZSBleHBvcnRlclNlcnZpY2U6IElFeHBvcnRlcixcbiAgICBASW5qZWN0KE9URUxfUFJPUEFHQVRPUilcbiAgICBwcml2YXRlIHByb3BhZ2F0b3JTZXJ2aWNlOiBJUHJvcGFnYXRvcixcbiAgICBAT3B0aW9uYWwoKSBASW5qZWN0KE9URUxfTE9HR0VSKVxuICAgIHByaXZhdGUgbG9nZ2VyOiBEaWFnTG9nZ2VyLFxuICAgIEBPcHRpb25hbCgpIEBJbmplY3QoT1RFTF9DVVNUT01fU1BBTilcbiAgICBwcml2YXRlIGN1c3RvbVNwYW46IEN1c3RvbVNwYW4sXG4gICAgcHJpdmF0ZSBwbGF0Zm9ybUxvY2F0aW9uOiBQbGF0Zm9ybUxvY2F0aW9uXG4gICkge1xuICAgIHRoaXMudHJhY2VyID0gbmV3IFdlYlRyYWNlclByb3ZpZGVyKHtcbiAgICAgIHNhbXBsZXI6IHRoaXMuZGVmaW5lUHJvYmFiaWxpdHlTYW1wbGVyKHRoaXMuY29udmVydFN0cmluZ1RvTnVtYmVyKGNvbmZpZy5jb21tb25Db25maWcucHJvYmFiaWxpdHlTYW1wbGVyKSksXG4gICAgICByZXNvdXJjZTogdGhpcy5sb2FkUmVzb3VyY2VBdHRyaWJ1dGVzKHRoaXMuY29uZmlnLmNvbW1vbkNvbmZpZyksXG4gICAgICBzcGFuUHJvY2Vzc29yczogdGhpcy5pbnNlcnRPck5vdFNwYW5FeHBvcnRlcigpXG4gICAgfSk7XG4gICAgdGhpcy5jb250ZXh0TWFuYWdlciA9IG5ldyBTdGFja0NvbnRleHRNYW5hZ2VyKCk7XG4gICAgdGhpcy50cmFjZXIucmVnaXN0ZXIoe1xuICAgICAgcHJvcGFnYXRvcjogdGhpcy5wcm9wYWdhdG9yU2VydmljZS5nZXRQcm9wYWdhdG9yKCksXG4gICAgICBjb250ZXh0TWFuYWdlcjogdGhpcy5jb250ZXh0TWFuYWdlclxuICAgIH0pO1xuICAgIHRoaXMubG9nQm9keSA9IGNvbmZpZy5jb21tb25Db25maWcubG9nQm9keTtcbiAgICBhcGkuZGlhZy5zZXRMb2dnZXIobG9nZ2VyLCBjb25maWcuY29tbW9uQ29uZmlnLmxvZ0xldmVsKTtcbiAgfVxuXG4gICAgLyoqXG4gICAqIE92ZXJpZGUgbWV0aG9kXG4gICAqIEludGVyY2VwdG9yIGZyb20gSHR0cEludGVyY2VwdG9yIEFuZ3VsYXJcbiAgICpcbiAgICogQHBhcmFtIHJlcXVlc3QgdGhlIGN1cnJlbnQgcmVxdWVzdFxuICAgKiBAcGFyYW0gbmV4dCBuZXh0XG4gICAqL1xuICAgIGludGVyY2VwdChcbiAgICAgIHJlcXVlc3Q6IEh0dHBSZXF1ZXN0PHVua25vd24+LFxuICAgICAgbmV4dDogSHR0cEhhbmRsZXJcbiAgICApOiBPYnNlcnZhYmxlPEh0dHBFdmVudDx1bmtub3duPj4ge1xuICAgICAgaWYgKGlzVXJsSWdub3JlZChyZXF1ZXN0LnVybCwgdGhpcy5jb25maWcuaWdub3JlVXJscz8udXJscykpIHtcbiAgICAgICAgcmV0dXJuIG5leHQuaGFuZGxlKHJlcXVlc3QpO1xuICAgICAgfVxuICAgICAgdGhpcy5jb250ZXh0TWFuYWdlci5kaXNhYmxlKCk7IC8vRklYIC0gcmVpbml0IGNvbnRleHRNYW5hZ2VyIGZvciBlYWNoIGh0dHAgY2FsbFxuICAgICAgdGhpcy5jb250ZXh0TWFuYWdlci5lbmFibGUoKTtcbiAgICAgIGNvbnN0IHNwYW46IFNwYW4gPSB0aGlzLmluaXRTcGFuKHJlcXVlc3QpO1xuICAgICAgY29uc3QgdHJhY2VkUmVxID0gdGhpcy5pbmplY3RDb250ZXh0QW5kSGVhZGVyKHJlcXVlc3QpO1xuICAgICAgcmV0dXJuIG5leHQuaGFuZGxlKHRyYWNlZFJlcSkucGlwZShcbiAgICAgICAgdGFwKFxuICAgICAgICAgIChldmVudDogSHR0cFJlc3BvbnNlPGFueT4pID0+IHtcbiAgICAgICAgICAgIHNwYW4uc2V0QXR0cmlidXRlcyhcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIFtBVFRSX0hUVFBfUkVTUE9OU0VfU1RBVFVTX0NPREVdOiBldmVudC5zdGF0dXMsXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBpZiAodGhpcy5sb2dCb2R5ICYmIGV2ZW50LmJvZHkgIT0gbnVsbCkge1xuICAgICAgICAgICAgICBzcGFuLmFkZEV2ZW50KCdyZXNwb25zZScsIHsgYm9keTogSlNPTi5zdHJpbmdpZnkoZXZlbnQuYm9keSkgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBzcGFuLnNldFN0YXR1cyh7XG4gICAgICAgICAgICAgIGNvZGU6IFNwYW5TdGF0dXNDb2RlLlVOU0VUXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHRoaXMuc2V0Q3VzdG9tU3BhbihzcGFuLCByZXF1ZXN0LCBldmVudCk7XG4gICAgICAgICAgfSxcbiAgICAgICAgICAoZXZlbnQ6IEh0dHBFcnJvclJlc3BvbnNlKSA9PiB7XG4gICAgICAgICAgICBzcGFuLnNldEF0dHJpYnV0ZXMoXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBbQVRUUl9IVFRQX1JFU1BPTlNFX1NUQVRVU19DT0RFXTogZXZlbnQuc3RhdHVzLFxuICAgICAgICAgICAgICAgIFtBVFRSX0VSUk9SX1RZUEVdIDogZXZlbnQubmFtZSxcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHNwYW4ucmVjb3JkRXhjZXB0aW9uKHtcbiAgICAgICAgICAgICAgbmFtZTogZXZlbnQubmFtZSxcbiAgICAgICAgICAgICAgbWVzc2FnZTogZXZlbnQubWVzc2FnZSxcbiAgICAgICAgICAgICAgc3RhY2s6IGV2ZW50LmVycm9yXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHNwYW4uc2V0U3RhdHVzKHtcbiAgICAgICAgICAgICAgY29kZTogU3BhblN0YXR1c0NvZGUuRVJST1JcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgdGhpcy5zZXRDdXN0b21TcGFuKHNwYW4sIHJlcXVlc3QsIGV2ZW50KTtcbiAgICAgICAgICB9XG4gICAgICAgICksXG4gICAgICAgIGZpbmFsaXplKCgpID0+IHtcbiAgICAgICAgICBzcGFuLmVuZCgpO1xuICAgICAgICAgIHRoaXMuY29udGV4dE1hbmFnZXIuZGlzYWJsZSgpO1xuICAgICAgICB9KVxuICAgICAgKTtcbiAgICB9XG5cbiAgLyoqXG4gICAqIEdldCBjdXJyZW50IHNjaGVtZSwgaG9zdG5hbWUgYW5kIHBvcnRcbiAgICovXG4gIHByaXZhdGUgZ2V0VVJMKCkge1xuICAgIHJldHVybiB0aGlzLnBsYXRmb3JtTG9jYXRpb24uaHJlZjtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZW5lcmF0ZSBSZXNvdXJjZSBBdHRyaWJ1dGVzXG4gICAqL1xuICBwcml2YXRlIGxvYWRSZXNvdXJjZUF0dHJpYnV0ZXMoXG4gICAgY29tbW9uQ29uZmlnOiBDb21tb25Db2xsZWN0b3JDb25maWdcbiAgKTogUmVzb3VyY2Uge1xuICAgIHJldHVybiByZXNvdXJjZUZyb21BdHRyaWJ1dGVzKHtcbiAgICAgIFtBVFRSX1NFUlZJQ0VfTkFNRV06IGNvbW1vbkNvbmZpZz8uc2VydmljZU5hbWUsXG4gICAgICAuLi5jb21tb25Db25maWc/LnJlc291cmNlQXR0cmlidXRlcyxcbiAgICB9KTtcbiAgfVxuICAvKipcbiAgICogSW5pdGlhbGlzZSBhIHNwYW4gZm9yIGEgcmVxdWVzdCBpbnRlcmNlcHRlZFxuICAgKlxuICAgKiBAcGFyYW0gcmVxdWVzdCByZXF1ZXN0XG4gICAqL1xuICBwcml2YXRlIGluaXRTcGFuKHJlcXVlc3Q6IEh0dHBSZXF1ZXN0PHVua25vd24+KTogU3BhbiB7XG4gICAgY29uc3QgdXJsUmVxdWVzdCA9IChyZXF1ZXN0LnVybFdpdGhQYXJhbXMuc3RhcnRzV2l0aCgnaHR0cCcpKSA/IG5ldyBVUkwocmVxdWVzdC51cmxXaXRoUGFyYW1zKSA6IG5ldyBVUkwodGhpcy5nZXRVUkwoKSk7XG4gICAgY29uc3Qgc3BhbiA9IHRoaXMudHJhY2VyXG4gICAgICAuZ2V0VHJhY2VyKGluZm9MaWJyYXJ5Lm5hbWUsIGluZm9MaWJyYXJ5LnZlcnNpb24pXG4gICAgICAuc3RhcnRTcGFuKFxuICAgICAgICBgJHtyZXF1ZXN0Lm1ldGhvZC50b1VwcGVyQ2FzZSgpfWAsXG4gICAgICAgIHtcbiAgICAgICAgICBhdHRyaWJ1dGVzOiB7XG4gICAgICAgICAgICBbQVRUUl9IVFRQX1JFUVVFU1RfTUVUSE9EXTogcmVxdWVzdC5tZXRob2QsXG4gICAgICAgICAgICBbQVRUUl9TRVJWRVJfQUREUkVTU106IHVybFJlcXVlc3QuaG9zdCxcbiAgICAgICAgICAgIFtBVFRSX1NFUlZFUl9QT1JUXTogdXJsUmVxdWVzdC5wb3J0LFxuICAgICAgICAgICAgW0FUVFJfVVJMX0ZVTExdOiByZXF1ZXN0LnVybFdpdGhQYXJhbXMsXG4gICAgICAgICAgICBbQVRUUl9VUkxfU0NIRU1FXTogdXJsUmVxdWVzdC5wcm90b2NvbC5yZXBsYWNlKCc6JywgJycpLFxuICAgICAgICAgICAgW0FUVFJfVVJMX1FVRVJZXTogdXJsUmVxdWVzdC5zZWFyY2gsXG4gICAgICAgICAgICBbQVRUUl9VU0VSX0FHRU5UX09SSUdJTkFMXTogd2luZG93Lm5hdmlnYXRvci51c2VyQWdlbnRcbiAgICAgICAgICB9LFxuICAgICAgICAgIGtpbmQ6IFNwYW5LaW5kLkNMSUVOVCxcbiAgICAgICAgfSxcbiAgICAgICAgdGhpcy5jb250ZXh0TWFuYWdlci5hY3RpdmUoKVxuICAgICAgKTtcbiAgICAvKmVzbGludCBuby11bmRlcnNjb3JlLWRhbmdsZTogW1wiZXJyb3JcIiwgeyBcImFsbG93XCI6IFtcIl9jdXJyZW50Q29udGV4dFwiXSB9XSovXG4gICAgdGhpcy5jb250ZXh0TWFuYWdlci5fY3VycmVudENvbnRleHQgPSBhcGkudHJhY2Uuc2V0U3BhbihcbiAgICAgIHRoaXMuY29udGV4dE1hbmFnZXIuYWN0aXZlKCksXG4gICAgICBzcGFuXG4gICAgKTtcbiAgICByZXR1cm4gc3BhbjtcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGQgaGVhZGVyIHByb3BhZ2F0b3IgaW4gcmVxdWVzdCBhbmQgY29uc2VydmUgb3JpZ2luYWwgaGVhZGVyXG4gICAqXG4gICAqIEBwYXJhbSByZXF1ZXN0IHJlcXVlc3RcbiAgICovXG4gIHByaXZhdGUgaW5qZWN0Q29udGV4dEFuZEhlYWRlcihcbiAgICByZXF1ZXN0OiBIdHRwUmVxdWVzdDx1bmtub3duPlxuICApIHtcbiAgICBjb25zdCBjYXJyaWVyID0ge307XG4gICAgYXBpLnByb3BhZ2F0aW9uLmluamVjdChcbiAgICAgIHRoaXMuY29udGV4dE1hbmFnZXIuYWN0aXZlKCksXG4gICAgICBjYXJyaWVyLFxuICAgICAgYXBpLmRlZmF1bHRUZXh0TWFwU2V0dGVyXG4gICAgKTtcbiAgICByZXF1ZXN0LmhlYWRlcnMua2V5cygpLm1hcChrZXkgPT4ge1xuICAgICAgY2FycmllcltrZXldID0gcmVxdWVzdC5oZWFkZXJzLmdldChrZXkpO1xuICAgIH0pO1xuICAgIHJldHVybiByZXF1ZXN0LmNsb25lKHtcbiAgICAgIHNldEhlYWRlcnM6IGNhcnJpZXIsXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogVmVyaWZ5IHRvIGluc2VydCBvciBub3QgYSBTcGFuIEV4cG9ydGVyXG4gICAqL1xuICBwcml2YXRlIGluc2VydE9yTm90U3BhbkV4cG9ydGVyKCkge1xuXG4gICAgaWYgKHRoaXMuZXhwb3J0ZXJTZXJ2aWNlLmdldEV4cG9ydGVyKCkgIT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIEFycmF5Lm9mKHRoaXMuaW5zZXJ0U3BhblByb2Nlc3NvclByb2R1Y3Rpb25Nb2RlKCksIHRoaXMuaW5zZXJ0Q29uc29sZVNwYW5FeHBvcnRlcigpKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIEFycmF5Lm9mKG5ldyBOb29wU3BhblByb2Nlc3NvcigpKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogSW5zZXJ0IGluIHRyYWNlciB0aGUgY29uc29sZSBzcGFuIGlmIGNvbmZpZyBpcyB0cnVlXG4gICAqL1xuICBwcml2YXRlIGluc2VydENvbnNvbGVTcGFuRXhwb3J0ZXIoKSB7XG4gICAgaWYgKHRoaXMuY29uZmlnLmNvbW1vbkNvbmZpZy5jb25zb2xlKSB7XG4gICAgICByZXR1cm4gbmV3IFNpbXBsZVNwYW5Qcm9jZXNzb3IobmV3IENvbnNvbGVTcGFuRXhwb3J0ZXIoKSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEluc2VydCBCYXRjaFNwYW5Qcm9jZXNzb3IgaW4gcHJvZHVjdGlvbiBtb2RlXG4gICAqIFNpbXBsZVNwYW5Qcm9jZXNzb3Igb3RoZXJ3aXNlXG4gICAqL1xuICBwcml2YXRlIGluc2VydFNwYW5Qcm9jZXNzb3JQcm9kdWN0aW9uTW9kZSgpIHtcbiAgICBjb25zdCBidWZmZXJDb25maWc6IEJ1ZmZlckNvbmZpZyA9IHtcbiAgICAgIG1heEV4cG9ydEJhdGNoU2l6ZTogdGhpcy5jb252ZXJ0U3RyaW5nVG9OdW1iZXIodGhpcy5jb25maWcuYmF0Y2hTcGFuUHJvY2Vzc29yQ29uZmlnPy5tYXhFeHBvcnRCYXRjaFNpemUpLFxuICAgICAgc2NoZWR1bGVkRGVsYXlNaWxsaXM6IHRoaXMuY29udmVydFN0cmluZ1RvTnVtYmVyKHRoaXMuY29uZmlnLmJhdGNoU3BhblByb2Nlc3NvckNvbmZpZz8uc2NoZWR1bGVkRGVsYXlNaWxsaXMpLFxuICAgICAgZXhwb3J0VGltZW91dE1pbGxpczogdGhpcy5jb252ZXJ0U3RyaW5nVG9OdW1iZXIodGhpcy5jb25maWcuYmF0Y2hTcGFuUHJvY2Vzc29yQ29uZmlnPy5leHBvcnRUaW1lb3V0TWlsbGlzKSxcbiAgICAgIG1heFF1ZXVlU2l6ZTogdGhpcy5jb252ZXJ0U3RyaW5nVG9OdW1iZXIodGhpcy5jb25maWcuYmF0Y2hTcGFuUHJvY2Vzc29yQ29uZmlnPy5tYXhRdWV1ZVNpemUpXG4gICAgfTtcbiAgICByZXR1cm4gdGhpcy5jb25maWcuY29tbW9uQ29uZmlnLnByb2R1Y3Rpb25cbiAgICAgICAgPyBuZXcgQmF0Y2hTcGFuUHJvY2Vzc29yKHRoaXMuZXhwb3J0ZXJTZXJ2aWNlLmdldEV4cG9ydGVyKCksIGJ1ZmZlckNvbmZpZylcbiAgICAgICAgOiBuZXcgU2ltcGxlU3BhblByb2Nlc3Nvcih0aGlzLmV4cG9ydGVyU2VydmljZS5nZXRFeHBvcnRlcigpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBkZWZpbmUgdGhlIFByb2JhYmlsaXR5IFNhbXBsZXJcbiAgICogQnkgRGVmYXVsdCwgaXQncyBhbHdheXMgKG9yIDEpXG4gICAqXG4gICAqIEBwYXJhbSBzYW1wbGVDb25maWcgdGhlIHNhbXBsZSBjb25maWd1cmF0aW9uXG4gICAqL1xuICBwcml2YXRlIGRlZmluZVByb2JhYmlsaXR5U2FtcGxlcihzYW1wbGVDb25maWc6IG51bWJlcik6IFNhbXBsZXIge1xuICAgIGlmIChzYW1wbGVDb25maWcgPj0gMSkge1xuICAgICAgcmV0dXJuIG5ldyBQYXJlbnRCYXNlZFNhbXBsZXIoeyByb290OiBuZXcgQWx3YXlzT25TYW1wbGVyKCkgfSk7XG4gICAgfVxuICAgIGVsc2UgaWYgKHNhbXBsZUNvbmZpZyA8PSAwIHx8IHNhbXBsZUNvbmZpZyA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gbmV3IFBhcmVudEJhc2VkU2FtcGxlcih7IHJvb3Q6IG5ldyBBbHdheXNPZmZTYW1wbGVyKCkgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBuZXcgUGFyZW50QmFzZWRTYW1wbGVyKHsgcm9vdDogbmV3IFRyYWNlSWRSYXRpb0Jhc2VkU2FtcGxlcihzYW1wbGVDb25maWcpIH0pO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBjb252ZXJ0IFN0cmluZyB0byBOdW1iZXIgKG9yIHVuZGVmaW5lZClcbiAgICpcbiAgICogQHBhcmFtIHZhbHVlXG4gICAqIEByZXR1cm5zIG51bWJlciBvciB1bmRlZmluZWRcbiAgICovXG4gIHByaXZhdGUgY29udmVydFN0cmluZ1RvTnVtYmVyKHZhbHVlOiBzdHJpbmcpOiBudW1iZXIge1xuICAgIHJldHVybiB2YWx1ZSAhPT0gdW5kZWZpbmVkID8gTnVtYmVyKHZhbHVlKSA6IHVuZGVmaW5lZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXQgY3VzdG9tIGF0dHJpYnV0ZXMgaW4gc3BhbiB3aXRoIGEgQ3VzdG9tU3BhblxuICAgKlxuICAgKiBAcGFyYW0gc3BhblxuICAgKiBAcGFyYW0gcmVxdWVzdFxuICAgKiBAcGFyYW0gcmVzcG9uc2VcbiAgICogQHJldHVybnMgU3BhblxuICAgKi9cbiAgcHJpdmF0ZSBzZXRDdXN0b21TcGFuKHNwYW46IFNwYW4sIHJlcXVlc3Q6IEh0dHBSZXF1ZXN0PHVua25vd24+LCByZXNwb25zZTogSHR0cFJlc3BvbnNlPHVua25vd24+IHwgSHR0cEVycm9yUmVzcG9uc2UpOiBTcGFuIHtcbiAgICByZXR1cm4gdGhpcy5jdXN0b21TcGFuICE9IG51bGwgPyB0aGlzLmN1c3RvbVNwYW4uYWRkKHNwYW4sIHJlcXVlc3QsIHJlc3BvbnNlKSA6IHNwYW47XG4gIH1cbn1cbiJdfQ==