ngx-logger
Version:
[](https://www.npmjs.com/package/ngx-logger)
208 lines • 32.3 kB
JavaScript
import { HttpRequest, HttpResponse } from '@angular/common/http';
import { Injectable, Optional } from '@angular/core';
import { of } from 'rxjs';
import { catchError, filter, map, retry, shareReplay } from 'rxjs/operators';
import * as vlq from 'vlq';
import * as i0 from "@angular/core";
import * as i1 from "@angular/common/http";
export class NGXLoggerMapperService {
constructor(httpBackend) {
this.httpBackend = httpBackend;
/** cache for source maps, key is source map location, ie. 'http://localhost:4200/main.js.map' */
this.sourceMapCache = new Map();
/** cache for specific log position, key is the dist position, ie 'main.js:339:21' */
this.logPositionCache = new Map();
}
/**
* Returns the log position of the caller
* If sourceMaps are enabled, it attemps to get the source map from the server, and use that to parse the position
* @param config
* @param metadata
* @returns
*/
getLogPosition(config, metadata) {
const stackLine = this.getStackLine(config);
// if we were not able to parse the stackLine, just return an empty Log Position
if (!stackLine) {
return of({ fileName: '', lineNumber: 0, columnNumber: 0 });
}
const logPosition = this.getLocalPosition(stackLine);
if (!config.enableSourceMaps) {
return of(logPosition);
}
const sourceMapLocation = this.getSourceMapLocation(stackLine);
return this.getSourceMap(sourceMapLocation, logPosition);
}
/**
* Get the stackline of the original caller
* @param config
* @returns null if stackline was not found
*/
getStackLine(config) {
const error = new Error();
try {
// noinspection ExceptionCaughtLocallyJS
throw error;
}
catch (e) {
try {
// Here are different examples of stacktrace
// Firefox (last line is the user code, the 4 first are ours):
// getStackLine@http://localhost:4200/main.js:358:23
// getCallerDetails@http://localhost:4200/main.js:557:44
// _log@http://localhost:4200/main.js:830:28
// debug@http://localhost:4200/main.js:652:14
// handleLog@http://localhost:4200/main.js:1158:29
// Chrome and Edge (last line is the user code):
// Error
// at Function.getStackLine (ngx-logger.js:329)
// at NGXMapperService.getCallerDetails (ngx-logger.js:528)
// at NGXLogger._log (ngx-logger.js:801)
// at NGXLogger.info (ngx-logger.js:631)
// at AppComponent.handleLog (app.component.ts:38)
let defaultProxy = 4; // We make 4 functions call before getting here
const firstStackLine = error.stack.split('\n')[0];
if (!firstStackLine.includes('.js:')) {
// The stacktrace starts with no function call (example in Chrome or Edge)
defaultProxy = defaultProxy + 1;
}
return error.stack.split('\n')[(defaultProxy + (config.proxiedSteps || 0))];
}
catch (e) {
return null;
}
}
}
/**
* Get position of caller without using sourceMaps
* @param stackLine
* @returns
*/
getLocalPosition(stackLine) {
// strip base path, then parse filename, line, and column, stackline looks like this :
// Firefox
// handleLog@http://localhost:4200/main.js:1158:29
// Chrome and Edge
// at AppComponent.handleLog (app.component.ts:38)
const positionStartIndex = stackLine.lastIndexOf('\/');
let positionEndIndex = stackLine.indexOf(')');
if (positionEndIndex < 0) {
positionEndIndex = undefined;
}
const position = stackLine.substring(positionStartIndex + 1, positionEndIndex);
const dataArray = position.split(':');
if (dataArray.length === 3) {
return { fileName: dataArray[0], lineNumber: +dataArray[1], columnNumber: +dataArray[2] };
}
return { fileName: 'unknown', lineNumber: 0, columnNumber: 0 };
}
getTranspileLocation(stackLine) {
// Example stackLine:
// Firefox : getStackLine@http://localhost:4200/main.js:358:23
// Chrome and Edge : at Function.getStackLine (ngx-logger.js:329)
let locationStartIndex = stackLine.indexOf('(');
if (locationStartIndex < 0) {
locationStartIndex = stackLine.lastIndexOf('@');
if (locationStartIndex < 0) {
locationStartIndex = stackLine.lastIndexOf(' ');
}
}
let locationEndIndex = stackLine.indexOf(')');
if (locationEndIndex < 0) {
locationEndIndex = undefined;
}
return stackLine.substring(locationStartIndex + 1, locationEndIndex);
}
/**
* Gets the URL of the sourcemap (the URL can be relative or absolute, it is browser dependant)
* @param stackLine
* @returns
*/
getSourceMapLocation(stackLine) {
const file = this.getTranspileLocation(stackLine);
const mapFullPath = file.substring(0, file.lastIndexOf(':'));
return mapFullPath.substring(0, mapFullPath.lastIndexOf(':')) + '.map';
}
getMapping(sourceMap, position) {
// => ';' indicates end of a line
// => ',' separates mappings in a line
// decoded mapping => [ generatedCodeColumn, sourceFileIndex, sourceCodeLine, sourceCodeColumn, nameIndex ]
let sourceFileIndex = 0, // second field
sourceCodeLine = 0, // third field
sourceCodeColumn = 0; // fourth field
const lines = sourceMap.mappings.split(';');
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
// reset column position to 0 after each line
let generatedCodeColumn = 0;
// decode sections in line
const columns = lines[lineIndex].split(',');
for (let columnIndex = 0; columnIndex < columns.length; columnIndex++) {
const decodedSection = vlq.decode(columns[columnIndex]);
if (decodedSection.length >= 4) {
// update relative positions
generatedCodeColumn += decodedSection[0];
sourceFileIndex += decodedSection[1];
sourceCodeLine += decodedSection[2];
sourceCodeColumn += decodedSection[3];
}
// check if matching map
if (lineIndex === position.lineNumber) {
if (generatedCodeColumn === position.columnNumber) {
// matching column and line found
return { fileName: sourceMap.sources[sourceFileIndex], lineNumber: sourceCodeLine, columnNumber: sourceCodeColumn };
}
else if (columnIndex + 1 === columns.length) {
// matching column not found, but line is correct
return { fileName: sourceMap.sources[sourceFileIndex], lineNumber: sourceCodeLine, columnNumber: 0 };
}
}
}
}
// failed if reached
return { fileName: 'unknown', lineNumber: 0, columnNumber: 0 };
}
/**
* does the http get request to get the source map
* @param sourceMapLocation
* @param distPosition
*/
getSourceMap(sourceMapLocation, distPosition) {
const req = new HttpRequest('GET', sourceMapLocation);
const distPositionKey = `${distPosition.fileName}:${distPosition.lineNumber}:${distPosition.columnNumber}`;
// if the specific log position is already in cache return it
if (this.logPositionCache.has(distPositionKey)) {
return this.logPositionCache.get(distPositionKey);
}
// otherwise check if the source map is already cached for given source map location
if (!this.sourceMapCache.has(sourceMapLocation)) {
if (!this.httpBackend) {
console.error('NGXLogger : Can\'t get sourcemap because HttpBackend is not provided. You need to import HttpClientModule');
this.sourceMapCache.set(sourceMapLocation, of(null));
}
else {
// obtain the source map if not cached
this.sourceMapCache.set(sourceMapLocation, this.httpBackend.handle(req).pipe(filter((e) => e instanceof HttpResponse), map((httpResponse) => httpResponse.body), retry(3), shareReplay(1)));
}
}
// at this point the source map is cached, use it to get specific log position mapping
const logPosition$ = this.sourceMapCache.get(sourceMapLocation).pipe(map((sourceMap) => {
// sourceMap can be null if HttpBackend is not provided for example
if (!sourceMap) {
return distPosition;
}
// map generated position to source position
return this.getMapping(sourceMap, distPosition);
}), catchError(() => of(distPosition)), shareReplay(1));
// store specific log position in cache for given dest position and return it
this.logPositionCache.set(distPositionKey, logPosition$);
return logPosition$;
}
}
/** @nocollapse */ NGXLoggerMapperService.ɵfac = function NGXLoggerMapperService_Factory(t) { return new (t || NGXLoggerMapperService)(i0.ɵɵinject(i1.HttpBackend, 8)); };
/** @nocollapse */ NGXLoggerMapperService.ɵprov = i0.ɵɵdefineInjectable({ token: NGXLoggerMapperService, factory: NGXLoggerMapperService.ɵfac });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(NGXLoggerMapperService, [{
type: Injectable
}], function () { return [{ type: i1.HttpBackend, decorators: [{
type: Optional
}] }]; }, null); })();
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFwcGVyLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbGliL21hcHBlci9tYXBwZXIuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQWUsV0FBVyxFQUFFLFlBQVksRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBRTlFLE9BQU8sRUFBRSxVQUFVLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ3JELE9BQU8sRUFBYyxFQUFFLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDdEMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLEtBQUssRUFBRSxXQUFXLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUM3RSxPQUFPLEtBQUssR0FBRyxNQUFNLEtBQUssQ0FBQzs7O0FBTzNCLE1BQU0sT0FBTyxzQkFBc0I7SUFRakMsWUFDc0IsV0FBd0I7UUFBeEIsZ0JBQVcsR0FBWCxXQUFXLENBQWE7UUFQOUMsaUdBQWlHO1FBQ3ZGLG1CQUFjLEdBQXVDLElBQUksR0FBRyxFQUFFLENBQUM7UUFFekUscUZBQXFGO1FBQzNFLHFCQUFnQixHQUFtRCxJQUFJLEdBQUcsRUFBRSxDQUFDO0lBS3ZGLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSSxjQUFjLENBQUMsTUFBd0IsRUFBRSxRQUE0QjtRQUMxRSxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBRTVDLGdGQUFnRjtRQUNoRixJQUFJLENBQUMsU0FBUyxFQUFFO1lBQ2QsT0FBTyxFQUFFLENBQUMsRUFBRSxRQUFRLEVBQUUsRUFBRSxFQUFFLFVBQVUsRUFBRSxDQUFDLEVBQUUsWUFBWSxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7U0FDN0Q7UUFFRCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLENBQUM7UUFFckQsSUFBSSxD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Q0FBQyxDQUFDLElBQUksQ0FDL0IsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLFlBQVksWUFBWSxDQUFDLEVBQ3hDLEdBQUcsQ0FDRCxDQUFDLFlBQXFDLEVBQUUsRUFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQzdELEVBQ0QsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUNSLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FDZixDQUNGLENBQUM7YUFDSDtTQUNGO1FBRUQsc0ZBQXNGO1FBQ3RGLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxjQUFjLENBQUMsR0FBRyxDQUFDLGlCQUFpQixDQUFDLENBQUMsSUFBSSxDQUNsRSxHQUFHLENBQW1DLENBQUMsU0FBUyxFQUFFLEVBQUU7WUFDbEQsbUVBQW1FO1lBQ25FLElBQUksQ0FBQyxTQUFTLEVBQUU7Z0JBQ2QsT0FBTyxZQUFZLENBQUM7YUFDckI7WUFDRCw0Q0FBNEM7WUFDNUMsT0FBTyxJQUFJLENBQUMsVUFBVSxDQUFDLFNBQVMsRUFBRSxZQUFZLENBQUMsQ0FBQTtRQUNqRCxDQUFDLENBQUMsRUFDRixVQUFVLENBQUMsR0FBRyxFQUFFLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxDQUFDLEVBQ2xDLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FDZixDQUFDO1FBRUYsNkVBQTZFO1FBQzdFLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsZUFBZSxFQUFFLFlBQVksQ0FBQyxDQUFDO1FBRXpELE9BQU8sWUFBWSxDQUFDO0lBQ3RCLENBQUM7OytHQTNPVSxzQkFBc0I7aUZBQXRCLHNCQUFzQixXQUF0QixzQkFBc0I7dUZBQXRCLHNCQUFzQjtjQURsQyxVQUFVOztzQkFVTixRQUFRIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSHR0cEJhY2tlbmQsIEh0dHBSZXF1ZXN0LCBIdHRwUmVzcG9uc2UgfSBmcm9tICdAYW5ndWxhci9jb21tb24vaHR0cCc7XHJcbmltcG9ydCB7IFNvdXJjZU1hcCB9IGZyb20gJ0Bhbmd1bGFyL2NvbXBpbGVyJztcclxuaW1wb3J0IHsgSW5qZWN0YWJsZSwgT3B0aW9uYWwgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHsgT2JzZXJ2YWJsZSwgb2YgfSBmcm9tICdyeGpzJztcclxuaW1wb3J0IHsgY2F0Y2hFcnJvciwgZmlsdGVyLCBtYXAsIHJldHJ5LCBzaGFyZVJlcGxheSB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcclxuaW1wb3J0ICogYXMgdmxxIGZyb20gJ3ZscSc7XHJcbmltcG9ydCB7IElOR1hMb2dnZXJDb25maWcgfSBmcm9tICcuLi9jb25maWcvaWNvbmZpZyc7XHJcbmltcG9ydCB7IElOR1hMb2dnZXJNZXRhZGF0YSB9IGZyb20gJy4uL21ldGFkYXRhL2ltZXRhZGF0YSc7XHJcbmltcG9ydCB7IElOR1hMb2dnZXJMb2dQb3NpdGlvbiB9IGZyb20gJy4vaWxvZy1wb3NpdGlvbic7XHJcbmltcG9ydCB7IElOR1hMb2dnZXJNYXBwZXJTZXJ2aWNlIH0gZnJvbSAnLi9pbWFwcGVyLnNlcnZpY2UnO1xyXG5cclxuQEluamVjdGFibGUoKVxyXG5leHBvcnQgY2xhc3MgTkdYTG9nZ2VyTWFwcGVyU2VydmljZSBpbXBsZW1lbnRzIElOR1hMb2dnZXJNYXBwZXJTZXJ2aWNlIHtcclxuXHJcbiAgLyoqIGNhY2hlIGZvciBzb3VyY2UgbWFwcywga2V5IGlzIHNvdXJjZSBtYXAgbG9jYXRpb24sIGllLiAnaHR0cDovL2xvY2FsaG9zdDo0MjAwL21haW4uanMubWFwJyAqL1xyXG4gIHByb3RlY3RlZCBzb3VyY2VNYXBDYWNoZTogTWFwPHN0cmluZywgT2JzZXJ2YWJsZTxTb3VyY2VNYXA+PiA9IG5ldyBNYXAoKTtcclxuXHJcbiAgLyoqIGNhY2hlIGZvciBzcGVjaWZpYyBsb2cgcG9zaXRpb24sIGtleSBpcyB0aGUgZGlzdCBwb3NpdGlvbiwgaWUgJ21haW4uanM6MzM5OjIxJyAqL1xyXG4gIHByb3RlY3RlZCBsb2dQb3NpdGlvbkNhY2hlOiBNYXA8c3RyaW5nLCBPYnNlcnZhYmxlPElOR1hMb2dnZXJMb2dQb3NpdGlvbj4+ID0gbmV3IE1hcCgpO1xyXG5cclxuICBjb25zdHJ1Y3RvcihcclxuICAgIEBPcHRpb25hbCgpIHByaXZhdGUgaHR0cEJhY2tlbmQ6IEh0dHBCYWNrZW5kXHJcbiAgKSB7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZXR1cm5zIHRoZSBsb2cgcG9zaXRpb24gb2YgdGhlIGNhbGxlclxyXG4gICAqIElmIHNvdXJjZU1hcHMgYXJlIGVuYWJsZWQsIGl0IGF0dGVtcHMgdG8gZ2V0IHRoZSBzb3VyY2UgbWFwIGZyb20gdGhlIHNlcnZlciwgYW5kIHVzZSB0aGF0IHRvIHBhcnNlIHRoZSBwb3NpdGlvblxyXG4gICAqIEBwYXJhbSBjb25maWcgXHJcbiAgICogQHBhcmFtIG1ldGFkYXRhIFxyXG4gICAqIEByZXR1cm5zIFxyXG4gICAqL1xyXG4gIHB1YmxpYyBnZXRMb2dQb3NpdGlvbihjb25maWc6IElOR1hMb2dnZXJDb25maWcsIG1ldGFkYXRhOiBJTkdYTG9nZ2VyTWV0YWRhdGEpOiBPYnNlcnZhYmxlPElOR1hMb2dnZXJMb2dQb3NpdGlvbj4ge1xyXG4gICAgY29uc3Qgc3RhY2tMaW5lID0gdGhpcy5nZXRTdGFja0xpbmUoY29uZmlnKTtcclxuXHJcbiAgICAvLyBpZiB3ZSB3ZXJlIG5vdCBhYmxlIHRvIHBhcnNlIHRoZSBzdGFja0xpbmUsIGp1c3QgcmV0dXJuIGFuIGVtcHR5IExvZyBQb3NpdGlvblxyXG4gICAgaWYgKCFzdGFja0xpbmUpIHtcclxuICAgICAgcmV0dXJuIG9mKHsgZmlsZU5hbWU6ICcnLCBsaW5lTnVtYmVyOiAwLCBjb2x1bW5OdW1iZXI6IDAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgY29uc3QgbG9nUG9zaXRpb24gPSB0aGlzLmdldExvY2FsUG9zaXRpb24oc3RhY2tMaW5lKTtcclxuXHJcbiAgICBpZiAoIWNvbmZpZy5lbmFibGVTb3VyY2VNYXBzKSB7XHJcbiAgICAgIHJldHVybiBvZihsb2dQb3NpdGlvbik7XHJcbiAgICB9XHJcblxyXG4gICAgY29uc3Qgc291cmNlTWFwTG9jYXRpb24gPSB0aGlzLmdldFNvdXJjZU1hcExvY2F0aW9uKHN0YWNrTGluZSk7XHJcbiAgICByZXR1cm4gdGhpcy5nZXRTb3VyY2VNYXAoc291cmNlTWFwTG9jYXRpb24sIGxvZ1Bvc2l0aW9uKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEdldCB0aGUgc3RhY2tsaW5lIG9mIHRoZSBvcmlnaW5hbCBjYWxsZXJcclxuICAgKiBAcGFyYW0gY29uZmlnIFxyXG4gICAqIEByZXR1cm5zIG51bGwgaWYgc3RhY2tsaW5lIHdhcyBub3QgZm91bmRcclxuICAgKi9cclxuICBwcm90ZWN0ZWQgZ2V0U3RhY2tMaW5lKGNvbmZpZzogSU5HWExvZ2dlckNvbmZpZyk6IHN0cmluZyB7XHJcbiAgICBjb25zdCBlcnJvciA9IG5ldyBFcnJvcigpO1xyXG5cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIG5vaW5zcGVjdGlvbiBFeGNlcHRpb25DYXVnaHRMb2NhbGx5SlNcclxuICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICB9IGNhdGNoIChlKSB7XHJcblxyXG4gICAgICB0cnkge1xyXG4gICAgICAgIC8vIEhlcmUgYXJlIGRpZmZlcmVudCBleGFtcGxlcyBvZiBzdGFja3RyYWNlIFxyXG5cclxuICAgICAgICAvLyBGaXJlZm94IChsYXN0IGxpbmUgaXMgdGhlIHVzZXIgY29kZSwgdGhlIDQgZmlyc3QgYXJlIG91cnMpOlxyXG4gICAgICAgIC8vIGdldFN0YWNrTGluZUBodHRwOi8vbG9jYWxob3N0OjQyMDAvbWFpbi5qczozNTg6MjNcclxuICAgICAgICAvLyBnZXRDYWxsZXJEZXRhaWxzQGh0dHA6Ly9sb2NhbGhvc3Q6NDIwMC9tYWluLmpzOjU1Nzo0NFxyXG4gICAgICAgIC8vIF9sb2dAaHR0cDovL2xvY2FsaG9zdDo0MjAwL21haW4uanM6ODMwOjI4XHJcbiAgICAgICAgLy8gZGVidWdAaHR0cDovL2xvY2FsaG9zdDo0MjAwL21haW4uanM6NjUyOjE0XHJcbiAgICAgICAgLy8gaGFuZGxlTG9nQGh0dHA6Ly9sb2NhbGhvc3Q6NDIwMC9tYWluLmpzOjExNTg6MjlcclxuXHJcbiAgICAgICAgLy8gQ2hyb21lIGFuZCBFZGdlIChsYXN0IGxpbmUgaXMgdGhlIHVzZXIgY29kZSk6XHJcbiAgICAgICAgLy8gRXJyb3JcclxuICAgICAgICAvLyBhdCBGdW5jdGlvbi5nZXRTdGFja0xpbmUgKG5neC1sb2dnZXIuanM6MzI5KVxyXG4gICAgICAgIC8vIGF0IE5HWE1hcHBlclNlcnZpY2UuZ2V0Q2FsbGVyRGV0YWlscyAobmd4LWxvZ2dlci5qczo1MjgpXHJcbiAgICAgICAgLy8gYXQgTkdYTG9nZ2VyLl9sb2cgKG5neC1sb2dnZXIuanM6ODAxKVxyXG4gICAgICAgIC8vIGF0IE5HWExvZ2dlci5pbmZvIChuZ3gtbG9nZ2VyLmpzOjYzMSlcclxuICAgICAgICAvLyBhdCBBcHBDb21wb25lbnQuaGFuZGxlTG9nIChhcHAuY29tcG9uZW50LnRzOjM4KVxyXG5cclxuICAgICAgICBsZXQgZGVmYXVsdFByb3h5ID0gNDsgLy8gV2UgbWFrZSA0IGZ1bmN0aW9ucyBjYWxsIGJlZm9yZSBnZXR0aW5nIGhlcmVcclxuICAgICAgICBjb25zdCBmaXJzdFN0YWNrTGluZSA9IGVycm9yLnN0YWNrLnNwbGl0KCdcXG4nKVswXTtcclxuICAgICAgICBpZiAoIWZpcnN0U3RhY2tMaW5lLmluY2x1ZGVzKCcuanM6JykpIHtcclxuICAgICAgICAgIC8vIFRoZSBzdGFja3RyYWNlIHN0YXJ0cyB3aXRoIG5vIGZ1bmN0aW9uIGNhbGwgKGV4YW1wbGUgaW4gQ2hyb21lIG9yIEVkZ2UpXHJcbiAgICAgICAgICBkZWZhdWx0UHJveHkgPSBkZWZhdWx0UHJveHkgKyAxO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmV0dXJuIGVycm9yLnN0YWNrLnNwbGl0KCdcXG4nKVsoZGVmYXVsdFByb3h5ICsgKGNvbmZpZy5wcm94aWVkU3RlcHMgfHwgMCkpXTtcclxuICAgICAgfSBjYXRjaCAoZSkge1xyXG4gICAgICAgIHJldHVybiBudWxsO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBHZXQgcG9zaXRpb24gb2YgY2FsbGVyIHdpdGhvdXQgdXNpbmcgc291cmNlTWFwc1xyXG4gICAqIEBwYXJhbSBzdGFja0xpbmUgXHJcbiAgICogQHJldHVybnMgXHJcbiAgICovXHJcbiAgcHJvdGVjdGVkIGdldExvY2FsUG9zaXRpb24oc3RhY2tMaW5lOiBzdHJpbmcpOiBJTkdYTG9nZ2VyTG9nUG9zaXRpb24ge1xyXG4gICAgLy8gc3RyaXAgYmFzZSBwYXRoLCB0aGVuIHBhcnNlIGZpbGVuYW1lLCBsaW5lLCBhbmQgY29sdW1uLCBzdGFja2xpbmUgbG9va3MgbGlrZSB0aGlzIDpcclxuICAgIC8vIEZpcmVmb3hcclxuICAgIC8vIGhhbmRsZUxvZ0BodHRwOi8vbG9jYWxob3N0OjQyMDAvbWFpbi5qczoxMTU4OjI5XHJcbiAgICAvLyBDaHJvbWUgYW5kIEVkZ2VcclxuICAgIC8vIGF0IEFwcENvbXBvbmVudC5oYW5kbGVMb2cgKGFwcC5jb21wb25lbnQudHM6MzgpXHJcblxyXG4gICAgY29uc3QgcG9zaXRpb25TdGFydEluZGV4ID0gc3RhY2tMaW5lLmxhc3RJbmRleE9mKCdcXC8nKTtcclxuICAgIGxldCBwb3NpdGlvbkVuZEluZGV4ID0gc3RhY2tMaW5lLmluZGV4T2YoJyknKTtcclxuICAgIGlmIChwb3NpdGlvbkVuZEluZGV4IDwgMCkge1xyXG4gICAgICBwb3NpdGlvbkVuZEluZGV4ID0gdW5kZWZpbmVkO1xyXG4gICAgfVxyXG5cclxuICAgIGNvbnN0IHBvc2l0aW9uID0gc3RhY2tMaW5lLnN1YnN0cmluZyhwb3NpdGlvblN0YXJ0SW5kZXggKyAxLCBwb3NpdGlvbkVuZEluZGV4KTtcclxuICAgIGNvbnN0IGRhdGFBcnJheSA9IHBvc2l0aW9uLnNwbGl0KCc6Jyk7XHJcbiAgICBpZiAoZGF0YUFycmF5Lmxlbmd0aCA9PT0gMykge1xyXG4gICAgICByZXR1cm4geyBmaWxlTmFtZTogZGF0YUFycmF5WzBdLCBsaW5lTnVtYmVyOiArZGF0YUFycmF5WzFdLCBjb2x1bW5OdW1iZXI6ICtkYXRhQXJyYXlbMl0gfTtcclxuICAgIH1cclxuICAgIHJldHVybiB7IGZpbGVOYW1lOiAndW5rbm93bicsIGxpbmVOdW1iZXI6IDAsIGNvbHVtbk51bWJlcjogMCB9O1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBnZXRUcmFuc3BpbGVMb2NhdGlvbihzdGFja0xpbmU6IHN0cmluZyk6IHN0cmluZyB7XHJcbiAgICAvLyBFeGFtcGxlIHN0YWNrTGluZTpcclxuICAgIC8vIEZpcmVmb3ggOiBnZXRTdGFja0xpbmVAaHR0cDovL2xvY2FsaG9zdDo0MjAwL21haW4uanM6MzU4OjIzXHJcbiAgICAvLyBDaHJvbWUgYW5kIEVkZ2UgOiBhdCBGdW5jdGlvbi5nZXRTdGFja0xpbmUgKG5neC1sb2dnZXIuanM6MzI5KVxyXG4gICAgbGV0IGxvY2F0aW9uU3RhcnRJbmRleCA9IHN0YWNrTGluZS5pbmRleE9mKCcoJyk7XHJcbiAgICBpZiAobG9jYXRpb25TdGFydEluZGV4IDwgMCkge1xyXG4gICAgICBsb2NhdGlvblN0YXJ0SW5kZXggPSBzdGFja0xpbmUubGFzdEluZGV4T2YoJ0AnKTtcclxuICAgICAgaWYgKGxvY2F0aW9uU3RhcnRJbmRleCA8IDApIHtcclxuICAgICAgICBsb2NhdGlvblN0YXJ0SW5kZXggPSBzdGFja0xpbmUubGFzdEluZGV4T2YoJyAnKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGxldCBsb2NhdGlvbkVuZEluZGV4ID0gc3RhY2tMaW5lLmluZGV4T2YoJyknKTtcclxuICAgIGlmIChsb2NhdGlvbkVuZEluZGV4IDwgMCkge1xyXG4gICAgICBsb2NhdGlvbkVuZEluZGV4ID0gdW5kZWZpbmVkO1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiBzdGFja0xpbmUuc3Vic3RyaW5nKGxvY2F0aW9uU3RhcnRJbmRleCArIDEsIGxvY2F0aW9uRW5kSW5kZXgpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogR2V0cyB0aGUgVVJMIG9mIHRoZSBzb3VyY2VtYXAgKHRoZSBVUkwgY2FuIGJlIHJlbGF0aXZlIG9yIGFic29sdXRlLCBpdCBpcyBicm93c2VyIGRlcGVuZGFudClcclxuICAgKiBAcGFyYW0gc3RhY2tMaW5lIFxyXG4gICAqIEByZXR1cm5zIFxyXG4gICAqL1xyXG4gIHByb3RlY3RlZCBnZXRTb3VyY2VNYXBMb2NhdGlvbihzdGFja0xpbmU6IHN0cmluZyk6IHN0cmluZyB7XHJcbiAgICBjb25zdCBmaWxlID0gdGhpcy5nZXRUcmFuc3BpbGVMb2NhdGlvbihzdGFja0xpbmUpO1xyXG4gICAgY29uc3QgbWFwRnVsbFBhdGggPSBmaWxlLnN1YnN0cmluZygwLCBmaWxlLmxhc3RJbmRleE9mKCc6JykpO1xyXG4gICAgcmV0dXJuIG1hcEZ1bGxQYXRoLnN1YnN0cmluZygwLCBtYXBGdWxsUGF0aC5sYXN0SW5kZXhPZignOicpKSArICcubWFwJztcclxuICB9XHJcblxyXG4gIHByaXZhdGUgZ2V0TWFwcGluZyhzb3VyY2VNYXA6IFNvdXJjZU1hcCwgcG9zaXRpb246IElOR1hMb2dnZXJMb2dQb3NpdGlvbik6IElOR1hMb2dnZXJMb2dQb3NpdGlvbiB7XHJcbiAgICAvLyA9PiAnOycgaW5kaWNhdGVzIGVuZCBvZiBhIGxpbmVcclxuICAgIC8vID0+ICcsJyBzZXBhcmF0ZXMgbWFwcGluZ3MgaW4gYSBsaW5lXHJcbiAgICAvLyBkZWNvZGVkIG1hcHBpbmcgPT4gWyBnZW5lcmF0ZWRDb2RlQ29sdW1uLCBzb3VyY2VGaWxlSW5kZXgsIHNvdXJjZUNvZGVMaW5lLCBzb3VyY2VDb2RlQ29sdW1uLCBuYW1lSW5kZXggXVxyXG4gICAgbGV0IHNvdXJjZUZpbGVJbmRleCA9IDAsICAgLy8gc2Vjb25kIGZpZWxkXHJcbiAgICAgIHNvdXJjZUNvZGVMaW5lID0gMCwgICAgLy8gdGhpcmQgZmllbGRcclxuICAgICAgc291cmNlQ29kZUNvbHVtbiA9IDA7ICAvLyBmb3VydGggZmllbGRcclxuXHJcbiAgICBjb25zdCBsaW5lcyA9IHNvdXJjZU1hcC5tYXBwaW5ncy5zcGxpdCgnOycpO1xyXG5cclxuICAgIGZvciAobGV0IGxpbmVJbmRleCA9IDA7IGxpbmVJbmRleCA8IGxpbmVzLmxlbmd0aDsgbGluZUluZGV4KyspIHtcclxuICAgICAgLy8gcmVzZXQgY29sdW1uIHBvc2l0aW9uIHRvIDAgYWZ0ZXIgZWFjaCBsaW5lXHJcbiAgICAgIGxldCBnZW5lcmF0ZWRDb2RlQ29sdW1uID0gMDtcclxuICAgICAgLy8gZGVjb2RlIHNlY3Rpb25zIGluIGxpbmVcclxuICAgICAgY29uc3QgY29sdW1ucyA9IGxpbmVzW2xpbmVJbmRleF0uc3BsaXQoJywnKTtcclxuXHJcbiAgICAgIGZvciAobGV0IGNvbHVtbkluZGV4ID0gMDsgY29sdW1uSW5kZXggPCBjb2x1bW5zLmxlbmd0aDsgY29sdW1uSW5kZXgrKykge1xyXG4gICAgICAgIGNvbnN0IGRlY29kZWRTZWN0aW9uID0gdmxxLmRlY29kZShjb2x1bW5zW2NvbHVtbkluZGV4XSk7XHJcbiAgICAgICAgaWYgKGRlY29kZWRTZWN0aW9uLmxlbmd0aCA+PSA0KSB7XHJcbiAgICAgICAgICAvLyB1cGRhdGUgcmVsYXRpdmUgcG9zaXRpb25zXHJcbiAgICAgICAgICBnZW5lcmF0ZWRDb2RlQ29sdW1uICs9IGRlY29kZWRTZWN0aW9uWzBdO1xyXG4gICAgICAgICAgc291cmNlRmlsZUluZGV4ICs9IGRlY29kZWRTZWN0aW9uWzFdO1xyXG4gICAgICAgICAgc291cmNlQ29kZUxpbmUgKz0gZGVjb2RlZFNlY3Rpb25bMl07XHJcbiAgICAgICAgICBzb3VyY2VDb2RlQ29sdW1uICs9IGRlY29kZWRTZWN0aW9uWzNdO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gY2hlY2sgaWYgbWF0Y2hpbmcgbWFwXHJcbiAgICAgICAgaWYgKGxpbmVJbmRleCA9PT0gcG9zaXRpb24ubGluZU51bWJlcikge1xyXG4gICAgICAgICAgaWYgKGdlbmVyYXRlZENvZGVDb2x1bW4gPT09IHBvc2l0aW9uLmNvbHVtbk51bWJlcikge1xyXG4gICAgICAgICAgICAvLyBtYXRjaGluZyBjb2x1bW4gYW5kIGxpbmUgZm91bmRcclxuICAgICAgICAgICAgcmV0dXJuIHsgZmlsZU5hbWU6IHNvdXJjZU1hcC5zb3VyY2VzW3NvdXJjZUZpbGVJbmRleF0sIGxpbmVOdW1iZXI6IHNvdXJjZUNvZGVMaW5lLCBjb2x1bW5OdW1iZXI6IHNvdXJjZUNvZGVDb2x1bW4gfTtcclxuICAgICAgICAgIH0gZWxzZSBpZiAoY29sdW1uSW5kZXggKyAxID09PSBjb2x1bW5zLmxlbmd0aCkge1xyXG4gICAgICAgICAgICAvLyBtYXRjaGluZyBjb2x1bW4gbm90IGZvdW5kLCBidXQgbGluZSBpcyBjb3JyZWN0XHJcbiAgICAgICAgICAgIHJldHVybiB7IGZpbGVOYW1lOiBzb3VyY2VNYXAuc291cmNlc1tzb3VyY2VGaWxlSW5kZXhdLCBsaW5lTnVtYmVyOiBzb3VyY2VDb2RlTGluZSwgY29sdW1uTnVtYmVyOiAwIH07XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICAvLyBmYWlsZWQgaWYgcmVhY2hlZFxyXG4gICAgcmV0dXJuIHsgZmlsZU5hbWU6ICd1bmtub3duJywgbGluZU51bWJlcjogMCwgY29sdW1uTnVtYmVyOiAwIH07XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBkb2VzIHRoZSBodHRwIGdldCByZXF1ZXN0IHRvIGdldCB0aGUgc291cmNlIG1hcFxyXG4gICAqIEBwYXJhbSBzb3VyY2VNYXBMb2NhdGlvblxyXG4gICAqIEBwYXJhbSBkaXN0UG9zaXRpb25cclxuICAgKi9cclxuICBwcm90ZWN0ZWQgZ2V0U291cmNlTWFwKHNvdXJjZU1hcExvY2F0aW9uOiBzdHJpbmcsIGRpc3RQb3NpdGlvbjogSU5HWExvZ2dlckxvZ1Bvc2l0aW9uKTogT2JzZXJ2YWJsZTxJTkdYTG9nZ2VyTG9nUG9zaXRpb24+IHtcclxuICAgIGNvbnN0IHJlcSA9IG5ldyBIdHRwUmVxdWVzdDxTb3VyY2VNYXA+KCdHRVQnLCBzb3VyY2VNYXBMb2NhdGlvbik7XHJcbiAgICBjb25zdCBkaXN0UG9zaXRpb25LZXkgPSBgJHtkaXN0UG9zaXRpb24uZmlsZU5hbWV9OiR7ZGlzdFBvc2l0aW9uLmxpbmVOdW1iZXJ9OiR7ZGlzdFBvc2l0aW9uLmNvbHVtbk51bWJlcn1gO1xyXG5cclxuICAgIC8vIGlmIHRoZSBzcGVjaWZpYyBsb2cgcG9zaXRpb24gaXMgYWxyZWFkeSBpbiBjYWNoZSByZXR1cm4gaXRcclxuICAgIGlmICh0aGlzLmxvZ1Bvc2l0aW9uQ2FjaGUuaGFzKGRpc3RQb3NpdGlvbktleSkpIHtcclxuICAgICAgcmV0dXJuIHRoaXMubG9nUG9zaXRpb25DYWNoZS5nZXQoZGlzdFBvc2l0aW9uS2V5KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBvdGhlcndpc2UgY2hlY2sgaWYgdGhlIHNvdXJjZSBtYXAgaXMgYWxyZWFkeSBjYWNoZWQgZm9yIGdpdmVuIHNvdXJjZSBtYXAgbG9jYXRpb25cclxuICAgIGlmICghdGhpcy5zb3VyY2VNYXBDYWNoZS5oYXMoc291cmNlTWFwTG9jYXRpb24pKSB7XHJcbiAgICAgIGlmICghdGhpcy5odHRwQmFja2VuZCkge1xyXG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ05HWExvZ2dlciA6IENhblxcJ3QgZ2V0IHNvdXJjZW1hcCBiZWNhdXNlIEh0dHBCYWNrZW5kIGlzIG5vdCBwcm92aWRlZC4gWW91IG5lZWQgdG8gaW1wb3J0IEh0dHBDbGllbnRNb2R1bGUnKTtcclxuICAgICAgICB0aGlzLnNvdXJjZU1hcENhY2hlLnNldChzb3VyY2VNYXBMb2NhdGlvbiwgb2YobnVsbCkpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIG9idGFpbiB0aGUgc291cmNlIG1hcCBpZiBub3QgY2FjaGVkXHJcbiAgICAgICAgdGhpcy5zb3VyY2VNYXBDYWNoZS5zZXQoXHJcbiAgICAgICAgICBzb3VyY2VNYXBMb2NhdGlvbixcclxuICAgICAgICAgIHRoaXMuaHR0cEJhY2tlbmQuaGFuZGxlKHJlcSkucGlwZShcclxuICAgICAgICAgICAgZmlsdGVyKChlKSA9PiBlIGluc3RhbmNlb2YgSHR0cFJlc3BvbnNlKSxcclxuICAgICAgICAgICAgbWFwPEh0dHBSZXNwb25zZTxTb3VyY2VNYXA+LCBTb3VyY2VNYXA+KFxyXG4gICAgICAgICAgICAgIChodHRwUmVzcG9uc2U6IEh0dHBSZXNwb25zZTxTb3VyY2VNYXA+KSA9PiBodHRwUmVzcG9uc2UuYm9keVxyXG4gICAgICAgICAgICApLFxyXG4gICAgICAgICAgICByZXRyeSgzKSxcclxuICAgICAgICAgICAgc2hhcmVSZXBsYXkoMSlcclxuICAgICAgICAgIClcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gYXQgdGhpcyBwb2ludCB0aGUgc291cmNlIG1hcCBpcyBjYWNoZWQsIHVzZSBpdCB0byBnZXQgc3BlY2lmaWMgbG9nIHBvc2l0aW9uIG1hcHBpbmdcclxuICAgIGNvbnN0IGxvZ1Bvc2l0aW9uJCA9IHRoaXMuc291cmNlTWFwQ2FjaGUuZ2V0KHNvdXJjZU1hcExvY2F0aW9uKS5waXBlKFxyXG4gICAgICBtYXA8U291cmNlTWFwLCBJTkdYTG9nZ2VyTG9nUG9zaXRpb24+KChzb3VyY2VNYXApID0+IHtcclxuICAgICAgICAvLyBzb3VyY2VNYXAgY2FuIGJlIG51bGwgaWYgSHR0cEJhY2tlbmQgaXMgbm90IHByb3ZpZGVkIGZvciBleGFtcGxlXHJcbiAgICAgICAgaWYgKCFzb3VyY2VNYXApIHtcclxuICAgICAgICAgIHJldHVybiBkaXN0UG9zaXRpb247XHJcbiAgICAgICAgfVxyXG4gICAgICAgIC8vIG1hcCBnZW5lcmF0ZWQgcG9zaXRpb24gdG8gc291cmNlIHBvc2l0aW9uXHJcbiAgICAgICAgcmV0dXJuIHRoaXMuZ2V0TWFwcGluZyhzb3VyY2VNYXAsIGRpc3RQb3NpdGlvbilcclxuICAgICAgfSksXHJcbiAgICAgIGNhdGNoRXJyb3IoKCkgPT4gb2YoZGlzdFBvc2l0aW9uKSksXHJcbiAgICAgIHNoYXJlUmVwbGF5KDEpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIHN0b3JlIHNwZWNpZmljIGxvZyBwb3NpdGlvbiBpbiBjYWNoZSBmb3IgZ2l2ZW4gZGVzdCBwb3NpdGlvbiBhbmQgcmV0dXJuIGl0XHJcbiAgICB0aGlzLmxvZ1Bvc2l0aW9uQ2FjaGUuc2V0KGRpc3RQb3NpdGlvbktleSwgbG9nUG9zaXRpb24kKTtcclxuXHJcbiAgICByZXR1cm4gbG9nUG9zaXRpb24kO1xyXG4gIH1cclxufVxyXG4iXX0=