UNPKG

ngx-logger

Version:

[![npm version](https://badge.fury.io/js/ngx-logger.svg)](https://www.npmjs.com/package/ngx-logger)

208 lines 32.3 kB
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=