ngx-endpoints
Version:
A library to dynamically load data from http endpoints / urls in angular
536 lines (526 loc) • 41 kB
JavaScript
import { __awaiter, __generator, __extends } from 'tslib';
import { Injectable, NgModule, defineInjectable, inject } from '@angular/core';
import { HttpClient, HttpHeaders, HttpClientModule } from '@angular/common/http';
import { catchError } from 'rxjs/operators';
import { throwError, BehaviorSubject } from 'rxjs';
import { parseUrl, stringify } from 'query-string';
import { relativism } from 'moment-relativism';
import * as moment_ from 'moment';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/** @type {?} */
var moment = moment_;
var NgxEndpointService = /** @class */ (function () {
function NgxEndpointService(_http) {
this._http = _http;
}
/**
* @return {?}
*/
NgxEndpointService.prototype.ngOnInit = /**
* @return {?}
*/
function () {
};
/**
* Gets data via http client
* @param endPointUrl url that will be requested
*/
/**
* Gets data via http client
* @template T
* @param {?} endPointUrl url that will be requested
* @param {?} httpheaders
* @return {?}
*/
NgxEndpointService.prototype.getData = /**
* Gets data via http client
* @template T
* @param {?} endPointUrl url that will be requested
* @param {?} httpheaders
* @return {?}
*/
function (endPointUrl, httpheaders) {
return __awaiter(this, void 0, void 0, function () {
var headers, key;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
headers = new HttpHeaders();
// tslint:disable-next-line:forin
// tslint:disable-next-line:forin
for (key in httpheaders) {
headers = headers.append(key, httpheaders[key]);
}
return [4 /*yield*/, this._http.get(endPointUrl, { headers: headers }).pipe(catchError(function (err) {
console.log('Cannot request data from ' + endPointUrl, err);
return throwError(err);
})).toPromise()];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
};
/**
* Conversion method for relative dates in querystrings
* @param endPointUrl return endpointurl with converted dates in querystring
*/
/**
* Conversion method for relative dates in querystrings
* @param {?} endPointUrl return endpointurl with converted dates in querystring
* @param {?} momentjsformat
* @return {?}
*/
NgxEndpointService.prototype.convertDatesInURL = /**
* Conversion method for relative dates in querystrings
* @param {?} endPointUrl return endpointurl with converted dates in querystring
* @param {?} momentjsformat
* @return {?}
*/
function (endPointUrl, momentjsformat) {
/** @type {?} */
var urlObject = parseUrl(endPointUrl);
for (var propertyName in urlObject.query) {
if (urlObject.query[propertyName].startsWith('now')) {
/** @type {?} */
var dateResult = relativism(urlObject.query[propertyName]);
urlObject.query[propertyName] = moment(dateResult).format(momentjsformat);
}
}
/** @type {?} */
var stringified = stringify(urlObject.query);
endPointUrl = urlObject.url + '?' + stringified;
return endPointUrl;
};
NgxEndpointService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */
NgxEndpointService.ctorParameters = function () { return [
{ type: HttpClient }
]; };
/** @nocollapse */ NgxEndpointService.ngInjectableDef = defineInjectable({ factory: function NgxEndpointService_Factory() { return new NgxEndpointService(inject(HttpClient)); }, token: NgxEndpointService, providedIn: "root" });
return NgxEndpointService;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/**
* Basic class that is used for the initial generation of the endpoints
*/
var /**
* Basic class that is used for the initial generation of the endpoints
*/
NgxEndPointData = /** @class */ (function () {
function NgxEndPointData() {
/**
* disable endpoint
*/
this.active = true;
/**
* Additional Headers (Api-Key, Basic Auth, Content-Type etc.)
*/
this.requestOptions = new Object();
/**
* Additional Headers (Api-Key, Basic Auth, Content-Type etc.)
*/
this.live = false;
/**
* Wait Interval between 2 live requests
*/
this.liveinterval = 10000;
/**
* If relative dates are used in querystring or specific query you can use this feature
*/
this.convertDates = false;
/**
* Replaces the relative date with the date in this output format in the querystring
*/
this.convertDatesOutputFormat = '';
}
return NgxEndPointData;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/**
* @template T
*/
var NgxEndPoint = /** @class */ (function () {
function NgxEndPoint(endpointservice, _endpoint) {
this.endpointservice = endpointservice;
this._endpoint = _endpoint;
/**
* data stores the last requested data as Behaviorsubject that can be subscribed.
*/
this.data = new BehaviorSubject(null);
/**
* if a request is already pending no additional request should be made to the datasource
*/
this.requestPending = new BehaviorSubject(false);
/**
* Is the endpoint alive and returns data ?
*/
this.isAlive = new BehaviorSubject(false);
/**
* Delivering internal process information
*/
this.events = new BehaviorSubject('');
/**
* for live sources this option will be used to stop the process
*/
this.running = true;
this.endpoint = _endpoint;
Object.assign(this.data, new Array());
}
/**
* Requests data from an http source
* Optionally converts relative dates (now-1d) into readable urls by DatesOutputFormat
* Adds the result of the request to BehaviorSubject data that can be subscribed
*/
/**
* Requests data from an http source
* Optionally converts relative dates (now-1d) into readable urls by DatesOutputFormat
* Adds the result of the request to BehaviorSubject data that can be subscribed
* @return {?}
*/
NgxEndPoint.prototype.requestData = /**
* Requests data from an http source
* Optionally converts relative dates (now-1d) into readable urls by DatesOutputFormat
* Adds the result of the request to BehaviorSubject data that can be subscribed
* @return {?}
*/
function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.requestInternal()];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
/**
* @return {?}
*/
NgxEndPoint.prototype.requestInternal = /**
* @return {?}
*/
function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!!this.requestPending.getValue()) return [3 /*break*/, 2];
this.addEvent('requesting data from endpoint', this.endpoint);
this.requestPending.next(true);
this.convertDates(this.endpoint.convertDates);
return [4 /*yield*/, this.request()];
case 1:
_a.sent();
this.requestPending.next(false);
return [3 /*break*/, 3];
case 2:
this.addEvent('a request is pending', this.endpoint);
_a.label = 3;
case 3: return [2 /*return*/];
}
});
});
};
/**
* @param {?} conversion
* @return {?}
*/
NgxEndPoint.prototype.convertDates = /**
* @param {?} conversion
* @return {?}
*/
function (conversion) {
if (conversion) {
this.endpoint.endPointUrl = this.endpointservice.convertDatesInURL(this.endpoint.endPointUrl, this.endpoint.convertDatesOutputFormat);
this.addEvent('converted endpointurl', this.endpoint);
}
};
/**
* @return {?}
*/
NgxEndPoint.prototype.request = /**
* @return {?}
*/
function () {
return __awaiter(this, void 0, void 0, function () {
var currentData, error_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4 /*yield*/, this.endpointservice.getData(this.endpoint.endPointUrl, this.endpoint.requestOptions)];
case 1:
currentData = _a.sent();
if (!this.isAlive.getValue() && currentData != null) {
this.isAlive.next(true);
this.addEvent('endpoint is alive', this.endpoint);
}
this.data.next(currentData);
this.addEvent('updated data', this.endpoint);
return [3 /*break*/, 3];
case 2:
error_1 = _a.sent();
this.addEvent(error_1, this.endpoint);
return [3 /*break*/, 3];
case 3: return [2 /*return*/];
}
});
});
};
/**
* @param {?} item
* @param {?} endpointdata
* @return {?}
*/
NgxEndPoint.prototype.addEvent = /**
* @param {?} item
* @param {?} endpointdata
* @return {?}
*/
function (item, endpointdata) {
this.events.next(JSON.stringify({ event: item, endpoint: endpointdata }));
};
NgxEndPoint.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */
NgxEndPoint.ctorParameters = function () { return [
{ type: NgxEndpointService },
{ type: NgxEndPointData }
]; };
/** @nocollapse */ NgxEndPoint.ngInjectableDef = defineInjectable({ factory: function NgxEndPoint_Factory() { return new NgxEndPoint(inject(NgxEndpointService), inject(NgxEndPointData)); }, token: NgxEndPoint, providedIn: "root" });
return NgxEndPoint;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/**
* @template T
*/
var NgxLiveEndPoint = /** @class */ (function (_super) {
__extends(NgxLiveEndPoint, _super);
function NgxLiveEndPoint(endpointservice, endpoint) {
var _this = _super.call(this, endpointservice, endpoint) || this;
_this.endpointservice = endpointservice;
_this.endpoint = endpoint;
return _this;
}
/**
* Requests data from an http source
* Optionally converts relative dates (now-1d) into readable urls by DatesOutputFormat
* Adds the result of the request to BehaviorSubject data that can be subscribed
*/
/**
* Requests data from an http source
* Optionally converts relative dates (now-1d) into readable urls by DatesOutputFormat
* Adds the result of the request to BehaviorSubject data that can be subscribed
* @return {?}
*/
NgxLiveEndPoint.prototype.requestData = /**
* Requests data from an http source
* Optionally converts relative dates (now-1d) into readable urls by DatesOutputFormat
* Adds the result of the request to BehaviorSubject data that can be subscribed
* @return {?}
*/
function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
this.running = true;
return [4 /*yield*/, this.refreshData()];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
/**
* internal function to wait before next live request will be made
*/
/**
* internal function to wait before next live request will be made
* @param {?} ms
* @return {?}
*/
NgxLiveEndPoint.prototype.sleep = /**
* internal function to wait before next live request will be made
* @param {?} ms
* @return {?}
*/
function (ms) {
return new Promise(function (resolve) { return setTimeout(resolve, ms); });
};
/**
* internal function to run live requests in loop
*/
/**
* internal function to run live requests in loop
* @return {?}
*/
NgxLiveEndPoint.prototype.refreshData = /**
* internal function to run live requests in loop
* @return {?}
*/
function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this.running) return [3 /*break*/, 3];
return [4 /*yield*/, this.requestInternal()];
case 1:
_a.sent();
return [4 /*yield*/, this.sleep(this.endpoint.liveinterval)];
case 2:
_a.sent();
return [3 /*break*/, 0];
case 3: return [2 /*return*/];
}
});
});
};
NgxLiveEndPoint.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */
NgxLiveEndPoint.ctorParameters = function () { return [
{ type: NgxEndpointService },
{ type: NgxEndPointData }
]; };
/** @nocollapse */ NgxLiveEndPoint.ngInjectableDef = defineInjectable({ factory: function NgxLiveEndPoint_Factory() { return new NgxLiveEndPoint(inject(NgxEndpointService), inject(NgxEndPointData)); }, token: NgxLiveEndPoint, providedIn: "root" });
return NgxLiveEndPoint;
}(NgxEndPoint));
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/**
* @template T
*/
var NgxEndPointDataProviderService = /** @class */ (function () {
function NgxEndPointDataProviderService(endpointservice) {
this.endpointservice = endpointservice;
this.endpoints = new Array();
}
/**
* Creates an NgxEndPoint and stores it in an array for proceeding
* @param NgxEndPointData endpointitem (basic json object)
* @returns the created NgxEndPoint for further proceeding
*/
/**
* Creates an NgxEndPoint and stores it in an array for proceeding
* @param {?} endpointitem
* @return {?} the created NgxEndPoint for further proceeding
*/
NgxEndPointDataProviderService.prototype.addEndPoint = /**
* Creates an NgxEndPoint and stores it in an array for proceeding
* @param {?} endpointitem
* @return {?} the created NgxEndPoint for further proceeding
*/
function (endpointitem) {
if (!endpointitem.endPointId) {
console.log('No endPointId provided. Please attach an endpointId');
throw new Error('No endPointId provided. Please attach an endpointId');
}
if (this.endpoints.find(function (x) { return x.endpoint.endPointId === endpointitem.endPointId; })) {
throw new Error('An endpoint with id ' + endpointitem.endPointId + ' has been already added. Please choose another id.');
}
/** @type {?} */
var endpoint;
if (endpointitem.live) {
endpoint = new NgxLiveEndPoint(this.endpointservice, endpointitem);
}
else {
endpoint = new NgxEndPoint(this.endpointservice, endpointitem);
}
this.endpoints.push(endpoint);
return endpoint;
};
/**
* Deletes a certain NgxEndPoint from endpoints array
* Stops running live requests
* @param endPointId Id of the specific NgxEndPoint
*/
/**
* Deletes a certain NgxEndPoint from endpoints array
* Stops running live requests
* @param {?} endPointId Id of the specific NgxEndPoint
* @return {?}
*/
NgxEndPointDataProviderService.prototype.destroyEndPoint = /**
* Deletes a certain NgxEndPoint from endpoints array
* Stops running live requests
* @param {?} endPointId Id of the specific NgxEndPoint
* @return {?}
*/
function (endPointId) {
if (this.endpoints && this.endpoints.length > 0) {
/** @type {?} */
var index = this.endpoints.findIndex(function (x) { return x.endpoint.endPointId === endPointId; });
this.endpoints[index].running = false;
this.endpoints.splice(index, 1);
}
};
NgxEndPointDataProviderService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */
NgxEndPointDataProviderService.ctorParameters = function () { return [
{ type: NgxEndpointService }
]; };
/** @nocollapse */ NgxEndPointDataProviderService.ngInjectableDef = defineInjectable({ factory: function NgxEndPointDataProviderService_Factory() { return new NgxEndPointDataProviderService(inject(NgxEndpointService)); }, token: NgxEndPointDataProviderService, providedIn: "root" });
return NgxEndPointDataProviderService;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
var NgxEndpointsModule = /** @class */ (function () {
function NgxEndpointsModule() {
}
NgxEndpointsModule.decorators = [
{ type: NgModule, args: [{
declarations: [],
imports: [HttpClientModule],
exports: [
HttpClientModule
],
providers: [NgxEndpointService, NgxEndPointDataProviderService, NgxEndPoint, NgxLiveEndPoint]
},] }
];
return NgxEndpointsModule;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
export { NgxEndpointsModule, NgxEndPointDataProviderService, NgxLiveEndPoint, NgxEndPoint, NgxEndpointService, NgxEndPointData };
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LWVuZHBvaW50cy5qcy5tYXAiLCJzb3VyY2VzIjpbIm5nOi8vbmd4LWVuZHBvaW50cy9saWIvbmd4LWVuZHBvaW50cy9uZ3gtZW5kcG9pbnRzLnNlcnZpY2UudHMiLCJuZzovL25neC1lbmRwb2ludHMvbGliL25neC1lbmRwb2ludHMvbmd4LWVuZHBvaW50LmZvcm1hdC50cyIsIm5nOi8vbmd4LWVuZHBvaW50cy9saWIvbmd4LWVuZHBvaW50cy9uZ3gtZW5kcG9pbnRzLnRzIiwibmc6Ly9uZ3gtZW5kcG9pbnRzL2xpYi9uZ3gtZW5kcG9pbnRzL25neC1saXZlLWVuZC1wb2ludHMvbmd4LWxpdmUtZW5kLXBvaW50cy50cyIsIm5nOi8vbmd4LWVuZHBvaW50cy9saWIvbmd4LWVuZHBvaW50cy9uZ3gtZW5kcG9pbnRzLWRhdGEtcHJvdmlkZXIuc2VydmljZS50cyIsIm5nOi8vbmd4LWVuZHBvaW50cy9saWIvbmd4LWVuZHBvaW50cy5tb2R1bGUudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSwgT25Jbml0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBIdHRwQ2xpZW50LCBIdHRwSGVhZGVycyB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbi9odHRwJztcbmltcG9ydCB7IGNhdGNoRXJyb3IgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyB0aHJvd0Vycm9yLCBvZiB9IGZyb20gJ3J4anMnO1xuaW1wb3J0ICogYXMgcXVlcnlTdHJpbmcgZnJvbSAncXVlcnktc3RyaW5nJztcbmltcG9ydCAqIGFzIG1vbWVudFJlbGF0aXZpc20gZnJvbSAnbW9tZW50LXJlbGF0aXZpc20nO1xuaW1wb3J0ICogYXMgbW9tZW50XyBmcm9tICdtb21lbnQnO1xuY29uc3QgbW9tZW50ID0gbW9tZW50XztcbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnXG59KVxuZXhwb3J0IGNsYXNzIE5neEVuZHBvaW50U2VydmljZSBpbXBsZW1lbnRzIE9uSW5pdCB7XG5cblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIF9odHRwOiBIdHRwQ2xpZW50KSB7IH1cblxuICBuZ09uSW5pdCgpIHtcbiAgfVxuICAvKipcbiAgICogR2V0cyBkYXRhIHZpYSBodHRwIGNsaWVudFxuICAgKiBAcGFyYW0gZW5kUG9pbnRVcmwgdXJsIHRoYXQgd2lsbCBiZSByZXF1ZXN0ZWRcbiAgICovXG4gIGFzeW5jIGdldERhdGE8VD4oZW5kUG9pbnRVcmw6IHN0cmluZywgaHR0cGhlYWRlcnM6IE9iamVjdCk6IFByb21pc2U8VD4ge1xuICAgIGxldCBoZWFkZXJzID0gbmV3IEh0dHBIZWFkZXJzKCk7XG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOmZvcmluXG5cbiAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6Zm9yaW5cbiAgICBmb3IgKGNvbnN0IGtleSBpbiBodHRwaGVhZGVycykge1xuICAgICAgaGVhZGVycyA9IGhlYWRlcnMuYXBwZW5kKGtleSwgaHR0cGhlYWRlcnNba2V5XSk7XG4gICAgfVxuICAgIHJldHVybiBhd2FpdCB0aGlzLl9odHRwLmdldDxUPihlbmRQb2ludFVybCwgeyBoZWFkZXJzOiBoZWFkZXJzIH0pLnBpcGU8VD4oXG4gICAgICBjYXRjaEVycm9yKChlcnIpID0+IHtcbiAgICAgICAgY29uc29sZS5sb2coJ0Nhbm5vdCByZXF1ZXN0IGRhdGEgZnJvbSAnICsgZW5kUG9pbnRVcmwsIGVycik7XG4gICAgICAgIHJldHVybiB0aHJvd0Vycm9yKGVycik7XG4gICAgfSlcbiAgICApLnRvUHJvbWlzZSgpO1xuICB9XG4gIC8qKlxuICAgKiBDb252ZXJzaW9uIG1ldGhvZCBmb3IgcmVsYXRpdmUgZGF0ZXMgaW4gcXVlcnlzdHJpbmdzXG4gICAqIEBwYXJhbSBlbmRQb2ludFVybCByZXR1cm4gZW5kcG9pbnR1cmwgd2l0aCBjb252ZXJ0ZWQgZGF0ZXMgaW4gcXVlcnlzdHJpbmdcbiAgICovXG4gIGNvbnZlcnREYXRlc0luVVJMKGVuZFBvaW50VXJsOiBzdHJpbmcsIG1vbWVudGpzZm9ybWF0OiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGNvbnN0IHVybE9iamVjdCA9IHF1ZXJ5U3RyaW5nLnBhcnNlVXJsKGVuZFBvaW50VXJsKTtcbiAgICBmb3IgKGNvbnN0IHByb3BlcnR5TmFtZSBpbiB1cmxPYmplY3QucXVlcnkpIHtcbiAgICAgIGlmICh1cmxPYmplY3QucXVlcnlbcHJvcGVydHlOYW1lXS5zdGFydHNXaXRoKCdub3cnKSkge1xuICAgICAgICBjb25zdCBkYXRlUmVzdWx0ID0gbW9tZW50UmVsYXRpdmlzbS5yZWxhdGl2aXNtKHVybE9iamVjdC5xdWVyeVtwcm9wZXJ0eU5hbWVdKTtcbiAgICAgICAgdXJsT2JqZWN0LnF1ZXJ5W3Byb3BlcnR5TmFtZV0gPSBtb21lbnQoZGF0ZVJlc3VsdCkuZm9ybWF0KG1vbWVudGpzZm9ybWF0KTtcbiAgICAgIH1cbiAgICB9XG4gICAgY29uc3Qgc3RyaW5naWZpZWQgPSBxdWVyeVN0cmluZy5zdHJpbmdpZnkodXJsT2JqZWN0LnF1ZXJ5KTtcbiAgICBlbmRQb2ludFVybCA9IHVybE9iamVjdC51cmwgKyAnPycgKyBzdHJpbmdpZmllZDtcbiAgICByZXR1cm4gZW5kUG9pbnRVcmw7XG4gIH1cblxufVxuIiwiICAvKipcbiAgKiBCYXNpYyBjbGFzcyB0aGF0IGlzIHVzZWQgZm9yIHRoZSBpbml0aWFsIGdlbmVyYXRpb24gb2YgdGhlIGVuZHBvaW50c1xuICAqL1xuZXhwb3J0IGNsYXNzIE5neEVuZFBvaW50RGF0YSB7XG4gICAvKipcbiAgKiBUaXRsZSBvZiB0aGUgZW5kcG9pbnRcbiAgKi9cbiAgICB0aXRsZTogc3RyaW5nO1xuICAvKipcbiAgKiBJZCBmb3IgbGF0ZXIgaWRlbnRpZmljYXRpb24gb2YgdGhlIGVuZHBvaW50XG4gICovXG4gICAgZW5kUG9pbnRJZDogTm9uTnVsbGFibGU8bnVtYmVyPjtcbiAgLyoqXG4gICogZGlzYWJsZSBlbmRwb2ludFxuICAqL1xuICAgIGFjdGl2ZSA9IHRydWU7XG4gIC8qKlxuICAqIEh0dHAgVXJsIHRoYXQgd2lsbCBiZSByZXF1ZXN0ZWQgYnkgRW5kcG9pbnQgKEh0dHAuR2V0KVxuICAqL1xuICAgIGVuZFBvaW50VXJsOiBzdHJpbmc7XG4gIC8qKlxuICAqIEFkZGl0aW9uYWwgSGVhZGVycyAoQXBpLUtleSwgQmFzaWMgQXV0aCwgQ29udGVudC1UeXBlIGV0Yy4pXG4gICovXG4gICAgcmVxdWVzdE9wdGlvbnMgPz0gbmV3IE9iamVjdCgpO1xuICAvKipcbiAgKiBBZGRpdGlvbmFsIEhlYWRlcnMgKEFwaS1LZXksIEJhc2ljIEF1dGgsIENvbnRlbnQtVHlwZSBldGMuKVxuICAqL1xuICAgIGxpdmUgPz0gZmFsc2U7XG4gIC8qKlxuICAqIFdhaXQgSW50ZXJ2YWwgYmV0d2VlbiAyIGxpdmUgcmVxdWVzdHNcbiAgKi9cbiAgICBsaXZlaW50ZXJ2YWwgPz0gMTAwMDA7XG4gIC8qKlxuICAqIElmIHJlbGF0aXZlIGRhdGVzIGFyZSB1c2VkIGluIHF1ZXJ5c3RyaW5nIG9yIHNwZWNpZmljIHF1ZXJ5IHlvdSBjYW4gdXNlIHRoaXMgZmVhdHVyZVxuICAqL1xuICAgIGNvbnZlcnREYXRlcyA/PSBmYWxzZTtcbiAgLyoqXG4gICogUmVwbGFjZXMgdGhlIHJlbGF0aXZlIGRhdGUgd2l0aCB0aGUgZGF0ZSBpbiB0aGlzIG91dHB1dCBmb3JtYXQgaW4gdGhlIHF1ZXJ5c3RyaW5nXG4gICovXG4gICAgY29udmVydERhdGVzT3V0cHV0Rm9ybWF0ID89ICcnO1xufVxuIiwiaW1wb3J0IHsgTmd4RW5kcG9pbnRTZXJ2aWNlIH0gZnJvbSAnLi9uZ3gtZW5kcG9pbnRzLnNlcnZpY2UnO1xuaW1wb3J0IHsgTmd4RW5kUG9pbnREYXRhIH0gZnJvbSAnLi9uZ3gtZW5kcG9pbnQuZm9ybWF0JztcbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgQ29tcG9uZW50LCBJbmplY3RhYmxlLCBDb21wb25lbnRSZWYgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuQEluamVjdGFibGUoe1xuICBwcm92aWRlZEluOiAncm9vdCdcbn0pXG5leHBvcnQgY2xhc3MgTmd4RW5kUG9pbnQ8VD4ge1xuICAvKipcbiAgKiByYXcgZGF0YSB0aGF0IGlzIHVzZWQgZm9yIGxhdGVyIGlkZW50aWZpY2F0aW9uIG9mIHRoZSBlbmRwb2ludFxuICAqL1xuICBwdWJsaWMgZW5kcG9pbnQ6IE5neEVuZFBvaW50RGF0YTtcbiAgLyoqXG4gICogZGF0YSBzdG9yZXMgdGhlIGxhc3QgcmVxdWVzdGVkIGRhdGEgYXMgQmVoYXZpb3JzdWJqZWN0IHRoYXQgY2FuIGJlIHN1YnNjcmliZWQuXG4gICovXG4gIHB1YmxpYyBkYXRhID0gbmV3IEJlaGF2aW9yU3ViamVjdDxUPihudWxsKTtcbiAgLyoqXG4gICogaWYgYSByZXF1ZXN0IGlzIGFscmVhZHkgcGVuZGluZyBubyBhZGRpdGlvbmFsIHJlcXVlc3Qgc2hvdWxkIGJlIG1hZGUgdG8gdGhlIGRhdGFzb3VyY2VcbiAgKi9cbiAgcHVibGljIHJlcXVlc3RQZW5kaW5nID0gbmV3IEJlaGF2aW9yU3ViamVjdDxhbnk+KGZhbHNlKTtcbiAgLyoqXG4gICogSXMgdGhlIGVuZHBvaW50IGFsaXZlIGFuZCByZXR1cm5zIGRhdGEgP1xuICAqL1xuICBwdWJsaWMgaXNBbGl2ZSA9IG5ldyBCZWhhdmlvclN1YmplY3Q8YW55PihmYWxzZSk7XG4gIC8qKlxuICAqIERlbGl2ZXJpbmcgaW50ZXJuYWwgcHJvY2VzcyBpbmZvcm1hdGlvblxuICAqL1xuICBwdWJsaWMgZXZlbnRzID0gbmV3IEJlaGF2aW9yU3ViamVjdDxzdHJpbmc+KCcnKTtcbiAgLyoqXG4gICogZm9yIGxpdmUgc291cmNlcyB0aGlzIG9wdGlvbiB3aWxsIGJlIHVzZWQgdG8gc3RvcCB0aGUgcHJvY2Vzc1xuICAqL1xuICBwdWJsaWMgcnVubmluZyA9IHRydWU7XG4gIGNvbnN0cnVjdG9yKHB1YmxpYyBlbmRwb2ludHNlcnZpY2U6IE5neEVuZHBvaW50U2VydmljZSwgcHJvdGVjdGVkIF9lbmRwb2ludDogTmd4RW5kUG9pbnREYXRhKSB7XG4gICAgdGhpcy5lbmRwb2ludCA9IF9lbmRwb2ludDtcbiAgICBPYmplY3QuYXNzaWduKHRoaXMuZGF0YSwgbmV3IEFycmF5PGFueT4oKSk7XG4gIH1cblxuICAvKipcbiAgKiBSZXF1ZXN0cyBkYXRhIGZyb20gYW4gaHR0cCBzb3VyY2VcbiAgKiBPcHRpb25hbGx5IGNvbnZlcnRzIHJlbGF0aXZlIGRhdGVzIChub3ctMWQpIGludG8gcmVhZGFibGUgdXJscyBieSBEYXRlc091dHB1dEZvcm1hdFxuICAqIEFkZHMgdGhlIHJlc3VsdCBvZiB0aGUgcmVxdWVzdCB0byBCZWhhdmlvclN1YmplY3QgZGF0YSB0aGF0IGNhbiBiZSBzdWJzY3JpYmVkXG4gICovXG4gIHB1YmxpYyBhc3luYyByZXF1ZXN0RGF0YSgpIHtcbiAgICBhd2FpdCB0aGlzLnJlcXVlc3RJbnRlcm5hbCgpO1xuICB9XG5cblxuICBwcm90ZWN0ZWQgYXN5bmMgcmVxdWVzdEludGVybmFsKCkge1xuICAgIGlmICghdGhpcy5yZXF1ZXN0UGVuZGluZy5nZXRWYWx1ZSgpKSB7XG4gICAgICB0aGlzLmFkZEV2ZW50KCdyZXF1ZXN0aW5nIGRhdGEgZnJvbSBlbmRwb2ludCcsIHRoaXMuZW5kcG9pbnQpO1xuICAgICAgdGhpcy5yZXF1ZXN0UGVuZGluZy5uZXh0KHRydWUpO1xuICAgICAgdGhpcy5jb252ZXJ0RGF0ZXModGhpcy5lbmRwb2ludC5jb252ZXJ0RGF0ZXMpO1xuICAgICAgYXdhaXQgdGhpcy5yZXF1ZXN0KCk7XG4gICAgICB0aGlzLnJlcXVlc3RQZW5kaW5nLm5leHQoZmFsc2UpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmFkZEV2ZW50KCdhIHJlcXVlc3QgaXMgcGVuZGluZycsIHRoaXMuZW5kcG9pbnQpO1xuXG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBjb252ZXJ0RGF0ZXMoY29udmVyc2lvbikge1xuICAgIGlmIChjb252ZXJzaW9uKSB7XG4gICAgICB0aGlzLmVuZHBvaW50LmVuZFBvaW50VXJsID0gdGhpcy5lbmRwb2ludHNlcnZpY2UuY29udmVydERhdGVzSW5VUkwodGhpcy5lbmRwb2ludC5lbmRQb2ludFVybCwgdGhpcy5lbmRwb2ludC5jb252ZXJ0RGF0ZXNPdXRwdXRGb3JtYXQpO1xuICAgICAgdGhpcy5hZGRFdmVudCgnY29udmVydGVkIGVuZHBvaW50dXJsJywgdGhpcy5lbmRwb2ludCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyByZXF1ZXN0KCkge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBjdXJyZW50RGF0YSA9IGF3YWl0IHRoaXMuZW5kcG9pbnRzZXJ2aWNlLmdldERhdGE8VD4odGhpcy5lbmRwb2ludC5lbmRQb2ludFVybCwgdGhpcy5lbmRwb2ludC5yZXF1ZXN0T3B0aW9ucyk7XG4gICAgICBpZiAoIXRoaXMuaXNBbGl2ZS5nZXRWYWx1ZSgpICYmIGN1cnJlbnREYXRhICE9IG51bGwpIHtcbiAgICAgICAgdGhpcy5pc0FsaXZlLm5leHQodHJ1ZSk7XG4gICAgICAgIHRoaXMuYWRkRXZlbnQoJ2VuZHBvaW50IGlzIGFsaXZlJywgdGhpcy5lbmRwb2ludCk7XG4gICAgICB9XG4gICAgICB0aGlzLmRhdGEubmV4dChjdXJyZW50RGF0YSk7XG4gICAgICB0aGlzLmFkZEV2ZW50KCd1cGRhdGVkIGRhdGEnLCB0aGlzLmVuZHBvaW50KTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgdGhpcy5hZGRFdmVudChlcnJvciwgdGhpcy5lbmRwb2ludCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBhZGRFdmVudChpdGVtOiBhbnksIGVuZHBvaW50ZGF0YTogTmd4RW5kUG9pbnREYXRhKSB7XG5cbiAgICB0aGlzLmV2ZW50cy5uZXh0KEpTT04uc3RyaW5naWZ5KHtldmVudDogaXRlbSwgZW5kcG9pbnQ6IGVuZHBvaW50ZGF0YX0pKTtcbiAgfVxufVxuIiwiaW1wb3J0IHsgIE9uRGVzdHJveSwgQ29tcG9uZW50LCBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyAgTmd4RW5kcG9pbnRTZXJ2aWNlIH0gZnJvbSAnLi4vbmd4LWVuZHBvaW50cy5zZXJ2aWNlJztcbmltcG9ydCB7ICBOZ3hFbmRQb2ludCB9IGZyb20gJy4uL25neC1lbmRwb2ludHMnO1xuaW1wb3J0IHsgIE5neEVuZFBvaW50RGF0YSB9IGZyb20gJy4uL25neC1lbmRwb2ludC5mb3JtYXQnO1xuXG5cbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnXG59KVxuZXhwb3J0IGNsYXNzIE5neExpdmVFbmRQb2ludDxUPiBleHRlbmRzIE5neEVuZFBvaW50PFQ+IHtcbiAgY29uc3RydWN0b3IocHVibGljIGVuZHBvaW50c2VydmljZTogTmd4RW5kcG9pbnRTZXJ2aWNlLCBwdWJsaWMgZW5kcG9pbnQ6IE5neEVuZFBvaW50RGF0YSkge1xuICAgIHN1cGVyKGVuZHBvaW50c2VydmljZSwgZW5kcG9pbnQpO1xuICB9XG4gIC8qKlxuICAqIFJlcXVlc3RzIGRhdGEgZnJvbSBhbiBodHRwIHNvdXJjZVxuICAqIE9wdGlvbmFsbHkgY29udmVydHMgcmVsYXRpdmUgZGF0ZXMgKG5vdy0xZCkgaW50byByZWFkYWJsZSB1cmxzIGJ5IERhdGVzT3V0cHV0Rm9ybWF0XG4gICogQWRkcyB0aGUgcmVzdWx0IG9mIHRoZSByZXF1ZXN0IHRvIEJlaGF2aW9yU3ViamVjdCBkYXRhIHRoYXQgY2FuIGJlIHN1YnNjcmliZWRcbiAgKi9cbiAgcHVibGljIGFzeW5jIHJlcXVlc3REYXRhKCkge1xuICAgdGhpcy5ydW5uaW5nID0gdHJ1ZTtcbiAgIGF3YWl0IHRoaXMucmVmcmVzaERhdGEoKTtcbiAgfVxuXG4gIC8qKlxuICAqIGludGVybmFsIGZ1bmN0aW9uIHRvIHdhaXQgYmVmb3JlIG5leHQgbGl2ZSByZXF1ZXN0IHdpbGwgYmUgbWFkZVxuICAqL1xuICBwcml2YXRlIHNsZWVwKG1zKSB7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKHJlc29sdmUgPT4gc2V0VGltZW91dChyZXNvbHZlLCBtcykpO1xuICB9XG4gIC8qKlxuICAqIGludGVybmFsIGZ1bmN0aW9uIHRvIHJ1biBsaXZlIHJlcXVlc3RzIGluIGxvb3BcbiAgKi9cbiAgcHJpdmF0ZSBhc3luYyByZWZyZXNoRGF0YSgpIHtcbiAgICB3aGlsZSAodGhpcy5ydW5uaW5nKSB7XG4gICAgICBhd2FpdCB0aGlzLnJlcXVlc3RJbnRlcm5hbCgpO1xuICAgICAgYXdhaXQgIHRoaXMuc2xlZXAodGhpcy5lbmRwb2ludC5saXZlaW50ZXJ2YWwpO1xuICAgIH1cbiB9XG59XG4iLCJpbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBOZ3hFbmRQb2ludCB9IGZyb20gJy4vbmd4LWVuZHBvaW50cyc7XG5pbXBvcnQgeyBOZ3hMaXZlRW5kUG9pbnQgfSBmcm9tICcuL25neC1saXZlLWVuZC1wb2ludHMvbmd4LWxpdmUtZW5kLXBvaW50cyc7XG5pbXBvcnQgeyBOZ3hFbmRQb2ludERhdGEgfSBmcm9tICcuL25neC1lbmRwb2ludC5mb3JtYXQnO1xuaW1wb3J0IHsgTmd4RW5kcG9pbnRTZXJ2aWNlIH0gZnJvbSAnLi9uZ3gtZW5kcG9pbnRzLnNlcnZpY2UnO1xuaW1wb3J0IHsgQXJndW1lbnRPdXRPZlJhbmdlRXJyb3IgfSBmcm9tICdyeGpzJztcbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnXG59KVxuZXhwb3J0IGNsYXNzIE5neEVuZFBvaW50RGF0YVByb3ZpZGVyU2VydmljZTxUIGV4dGVuZHMgTmd4RW5kUG9pbnQ8YW55Pj4ge1xuICBwdWJsaWMgZW5kcG9pbnRzID0gbmV3IEFycmF5PFQ+KCk7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgZW5kcG9pbnRzZXJ2aWNlOiBOZ3hFbmRwb2ludFNlcnZpY2UpIHsgfVxuICAvKipcbiAgICogQ3JlYXRlcyBhbiBOZ3hFbmRQb2ludCBhbmQgc3RvcmVzIGl0IGluIGFuIGFycmF5IGZvciBwcm9jZWVkaW5nXG4gICAqIEBwYXJhbSBOZ3hFbmRQb2ludERhdGEgZW5kcG9pbnRpdGVtIChiYXNpYyBqc29uIG9iamVjdClcbiAgICogQHJldHVybnMgdGhlIGNyZWF0ZWQgTmd4RW5kUG9pbnQgZm9yIGZ1cnRoZXIgcHJvY2VlZGluZ1xuICAgKi9cbiAgYWRkRW5kUG9pbnQoZW5kcG9pbnRpdGVtOiBOZ3hFbmRQb2ludERhdGEpOiBOZ3hFbmRQb2ludDxhbnk+IHtcbiAgICBpZiAoIWVuZHBvaW50aXRlbS5lbmRQb2ludElkKSB7XG4gICAgICBjb25zb2xlLmxvZygnTm8gZW5kUG9pbnRJZCBwcm92aWRlZC4gUGxlYXNlIGF0dGFjaCBhbiBlbmRwb2ludElkJyk7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIGVuZFBvaW50SWQgcHJvdmlkZWQuIFBsZWFzZSBhdHRhY2ggYW4gZW5kcG9pbnRJZCcpO1xuICAgIH1cbiAgICBpZiAodGhpcy5lbmRwb2ludHMuZmluZCh4ID0+IHguZW5kcG9pbnQuZW5kUG9pbnRJZCA9PT0gZW5kcG9pbnRpdGVtLmVuZFBvaW50SWQpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0FuIGVuZHBvaW50IHdpdGggaWQgJyArIGVuZHBvaW50aXRlbS5lbmRQb2ludElkICsgJyBoYXMgYmVlbiBhbHJlYWR5IGFkZGVkLiBQbGVhc2UgY2hvb3NlIGFub3RoZXIgaWQuJyk7XG4gICAgfVxuICAgIGxldCBlbmRwb2ludDtcbiAgICBpZiAoZW5kcG9pbnRpdGVtLmxpdmUpIHtcbiAgICAgIGVuZHBvaW50ID0gbmV3IE5neExpdmVFbmRQb2ludCh0aGlzLmVuZHBvaW50c2VydmljZSwgZW5kcG9pbnRpdGVtKTtcbiAgICB9IGVsc2Uge1xuICAgICAgZW5kcG9pbnQgPSBuZXcgTmd4RW5kUG9pbnQodGhpcy5lbmRwb2ludHNlcnZpY2UsIGVuZHBvaW50aXRlbSk7XG4gICAgfVxuICAgIHRoaXMuZW5kcG9pbnRzLnB1c2goZW5kcG9pbnQpO1xuICAgIHJldHVybiBlbmRwb2ludDtcbiAgfVxuICAvKipcbiAgICogRGVsZXRlcyBhIGNlcnRhaW4gTmd4RW5kUG9pbnQgZnJvbSBlbmRwb2ludHMgYXJyYXlcbiAgICogU3RvcHMgcnVubmluZyBsaXZlIHJlcXVlc3RzXG4gICAqIEBwYXJhbSBlbmRQb2ludElkIElkIG9mIHRoZSBzcGVjaWZpYyBOZ3hFbmRQb2ludFxuICAgKi9cbiAgZGVzdHJveUVuZFBvaW50KGVuZFBvaW50SWQ6IG51bWJlcikge1xuICAgIGlmICh0aGlzLmVuZHBvaW50cyAmJiB0aGlzLmVuZHBvaW50cy5sZW5ndGggPiAwKSB7XG4gICAgICBjb25zdCBpbmRleCA9IHRoaXMuZW5kcG9pbnRzLmZpbmRJbmRleCh4ID0+IHguZW5kcG9pbnQuZW5kUG9pbnRJZCA9PT0gZW5kUG9pbnRJZCk7XG4gICAgICB0aGlzLmVuZHBvaW50c1tpbmRleF0ucnVubmluZyA9IGZhbHNlO1xuICAgICAgdGhpcy5lbmRwb2ludHMuc3BsaWNlKGluZGV4LCAxKTtcbiAgICB9XG4gIH1cblxuXG5cbn1cbiIsImltcG9ydCB7IE5nTW9kdWxlLCBNb2R1bGVXaXRoUHJvdmlkZXJzICB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgSHR0cENsaWVudE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbi9odHRwJztcbmltcG9ydCB7IE5neEVuZFBvaW50RGF0YVByb3ZpZGVyU2VydmljZX0gZnJvbSAnLi9uZ3gtZW5kcG9pbnRzL25neC1lbmRwb2ludHMtZGF0YS1wcm92aWRlci5zZXJ2aWNlJztcbmltcG9ydCB7IE5neEVuZHBvaW50U2VydmljZX0gZnJvbSAnLi9uZ3gtZW5kcG9pbnRzL25neC1lbmRwb2ludHMuc2VydmljZSc7XG5pbXBvcnQgeyBOZ3hFbmRQb2ludH0gZnJvbSAnLi9uZ3gtZW5kcG9pbnRzL25neC1lbmRwb2ludHMnO1xuaW1wb3J0IHsgTmd4TGl2ZUVuZFBvaW50fSBmcm9tICcuL25neC1lbmRwb2ludHMvbmd4LWxpdmUtZW5kLXBvaW50cy9uZ3gtbGl2ZS1lbmQtcG9pbnRzJztcbkBOZ01vZHVsZSh7XG4gIGRlY2xhcmF0aW9uczogW10sXG4gIGltcG9ydHM6IFtIdHRwQ2xpZW50TW9kdWxlXSxcbiAgZXhwb3J0czogW1xuICAgIEh0dHBDbGllbnRNb2R1bGVcbiAgXSxcbiAgcHJvdmlkZXJzOiBbTmd4RW5kcG9pbnRTZXJ2aWNlLCBOZ3hFbmRQb2ludERhdGFQcm92aWRlclNlcnZpY2UsIE5neEVuZFBvaW50LCBOZ3hMaXZlRW5kUG9pbnRdXG59KVxuZXhwb3J0IGNsYXNzIE5neEVuZHBvaW50c01vZHVsZSB7IH1cbiJdLCJuYW1lcyI6WyJxdWVyeVN0cmluZy5wYXJzZVVybCIsIm1vbWVudFJlbGF0aXZpc20ucmVsYXRpdmlzbSIsInF1ZXJ5U3RyaW5nLnN0cmluZ2lmeSIsInRzbGliXzEuX19leHRlbmRzIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7OztJQU9NLE1BQU0sR0FBRyxPQUFPO0FBQ3RCO0lBTUUsNEJBQW9CLEtBQWlCO1FBQWpCLFVBQUssR0FBTCxLQUFLLENBQVk7S0FBSzs7OztJQUUxQyxxQ0FBUTs7O0lBQVI7S0FDQzs7Ozs7Ozs7Ozs7O0lBS0ssb0NBQU87Ozs7Ozs7SUFBYixVQUFpQixXQUFtQixFQUFFLFdBQW1COzs7Ozs7d0JBQ25ELE9BQU8sR0FBRyxJQUFJLFdBQVcsRUFBRTs7O3dCQUkvQixLQUFXLEdBQUcsSUFBSSxXQUFXLEVBQUU7NEJBQzdCLE9BQU8sR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFDLEdBQUcsRUFBRSxXQUFXLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQzt5QkFDakQ7d0JBQ00scUJBQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUksV0FBVyxFQUFFLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUMsSUFBSSxDQUNwRSxVQUFVLENBQUMsVUFBQyxHQUFHO2dDQUNiLE9BQU8sQ0FBQyxHQUFHLENBQUMsMkJBQTJCLEdBQUcsV0FBVyxFQUFFLEdBQUcsQ0FBQyxDQUFD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