@synerty/vortexjs
Version:
Custom observable data serialisation and routing based on Angular 2+
238 lines • 34.6 kB
JavaScript
import { Inject, Injectable } from "@angular/core";
import { dateStr } from "../UtilMisc";
import { TupleStorageServiceABC, } from "./TupleStorageServiceABC";
import { TupleOfflineStorageNameService } from "./TupleOfflineStorageNameService";
import { addIndexedDbHandlers, IDBException, indexedDB, } from "../storage-api/indexeddb-api";
import * as i0 from "@angular/core";
// ----------------------------------------------------------------------------
const noSpaceMsgs = [
"there was not enough remaining storage space",
"QuotaExceededError",
];
const noSpaceRetryMs = 5 * 1000;
const indexDbNotOpenMsg = "IndexedDB peek_plugin_diagram_grids is not open";
function now() {
return new Date();
}
const TUPLE_STORE = "tuples";
/** Tuple Storage IndexedDB
*
* This class handles storing and retrieving tuples to/from indexed db.
*
*/
export class TupleStorageIndexedDbService extends TupleStorageServiceABC {
name;
db;
openInProgressPromise = null;
constructor(name) {
super(name);
this.name = name;
}
// ----------------------------------------------------------------------------
// Open the indexed db database
open() {
if (this.openInProgressPromise != null)
return this.openInProgressPromise;
this.openInProgressPromise = new Promise((resolve, reject) => {
// DISP Store
let request = indexedDB.open(this.dbName, 1);
addIndexedDbHandlers(request, () => {
let msg = `${dateStr()} IndexedDB : "${this.dbName}" ` +
`Failed to open IndexedDB database`;
this.openInProgressPromise = null;
reject(msg);
throw new IDBException(msg);
});
request.onsuccess = (event) => {
console.log(`${dateStr()} IndexedDB : "${this.dbName}" Success opening DB`);
if (this.db == null) {
this.db = event.target.result;
this.openInProgressPromise = null;
resolve();
}
};
request.onupgradeneeded = (event) => {
console.log(`${dateStr()} IndexedDB : "${this.dbName}" Upgrading`);
let db = event.target.result;
// SCHEMA for database points
// Schema Version 1
db.createObjectStore(TUPLE_STORE, { keyPath: "tupleSelector" });
console.log(`${dateStr()} IndexedDB : "${this.dbName}" Upgrade Success`);
};
});
return this.openInProgressPromise;
}
// ----------------------------------------------------------------------------
// Check if the DB is open
async isOpen() {
return this.db != null;
}
async close() {
if (!(await this.isOpen())) {
throw new Error(`IndexedDB "${this.dbName}" is not open`);
}
this.db.close();
this.db = null;
}
async truncateStorage() {
if (!(await this.isOpen()))
await this.open();
let startTime = now();
await new Promise((resolve, reject) => {
let response = indexedDB.deleteDatabase(this.dbName);
addIndexedDbHandlers(response, () => {
reject(`${dateStr()} IndexedDB: truncateStorage "truncateStorage" error`);
throw new IDBException("deleteDatabase error");
});
response.oncomplete = () => {
let timeTaken = now() - startTime;
console.log(`${dateStr()} IndexedDB: truncateStorage` +
` took ${timeTaken}ms (in thread)`);
resolve();
};
});
}
objectStoreTx(forWrite) {
// Get the Read Only case out the way, it's easy
const mode = forWrite ? "readwrite" : "readonly";
const tx = this.db.transaction(TUPLE_STORE, mode);
return tx.objectStore(TUPLE_STORE);
}
// ----------------------------------------------------------------------------
// Load the display items from the cache
// ----------------------------------------------------------------------------
// Load the display items from the cache
async loadTuplesEncoded(tupleSelector) {
if (!(await this.isOpen()))
await this.open();
let startTime = now();
return await new Promise((resolve, reject) => {
const request = this.objectStoreTx(false) //
.get(tupleSelector.toOrderedJsonStr());
addIndexedDbHandlers(request, () => {
let msg = `${dateStr()} IndexedDB: Index open cursor`;
reject(msg);
throw new IDBException(msg);
});
request.onsuccess = () => {
let timeTaken = now() - startTime;
console.log(`${dateStr()} IndexedDB: loadTuples took ${timeTaken}ms (in thread)`);
// Called for each matching record
let data = request.result;
if (data == null) {
resolve(null);
return;
}
resolve(data.payload);
};
});
}
async batchSaveTuplesEncoded(data) {
if (!(await this.isOpen()))
await this.open();
// The payload is a convenient way to serialise and compress the data
const items = [];
const nowDate = new Date();
for (const data_ of data) {
{
items.push({
tupleSelector: data_.tupleSelector.toOrderedJsonStr(),
dateTime: nowDate,
payload: data_.vortexMsg,
});
}
}
const startTime = now();
while (true) {
try {
// Run the inserts
const store = this.objectStoreTx(true);
const promises = [];
for (const item of items) {
promises.push(new Promise((res2, rej2) => {
const response = store.put(item);
addIndexedDbHandlers(response, () => {
rej2(`${dateStr()} IndexedDB: saveTuplesEncoded "put" error`);
throw new IDBException("Put error");
});
response.onsuccess = () => {
const timeTaken = now() - startTime;
console.log(`${dateStr()} IndexedDB: saveTuplesEncoded` +
` took ${timeTaken}ms (in thread)` +
` Inserted/updated ${item.payload?.length} of encoding`);
res2();
};
}));
}
await Promise.all(promises);
return;
}
catch (e) {
if (this.isNoSpaceMessage(e)) {
console.log(`${dateStr()} IndexedDB: ${this.name.name}` +
" saveTuplesEncoded" +
" Waiting for space quote extension");
await new Promise((r) => setTimeout(() => r(), noSpaceRetryMs));
continue;
}
throw new Error(e);
}
}
}
// ----------------------------------------------------------------------------
// Add disply items to the cache
async saveTuplesEncoded(tupleSelector, vortexMsg) {
await this.batchSaveTuplesEncoded([
{ tupleSelector: tupleSelector, vortexMsg: vortexMsg },
]);
}
async deleteTuples(tupleSelector) {
if (!(await this.isOpen()))
await this.open();
// The payload is a convenient way to serialise and compress the data
const tupleSelectorStr = tupleSelector.toOrderedJsonStr();
const startTime = now();
await new Promise((resolve, reject) => {
// Run the inserts
const response = this.objectStoreTx(true) //
.delete(tupleSelectorStr);
addIndexedDbHandlers(response, () => {
reject(`${dateStr()} IndexedDB: deleteTuples "delete" error`);
throw new IDBException("Put error");
});
response.onsuccess = () => {
let timeTaken = now() - startTime;
console.log(`${dateStr()} IndexedDB: deleteTuples` +
` took ${timeTaken}ms (in thread)`);
resolve();
};
});
}
async deleteOldTuples(deleteDataBeforeDate) {
if (!(await this.isOpen()))
await this.open();
console.log(`${dateStr()} WARNING: deleteOldTuple not implemented for IndexedDB`);
return Promise.resolve();
}
// noinspection JSMethodCanBeStatic
isNoSpaceMessage(message) {
for (const msg of noSpaceMsgs) {
if (message.indexOf(msg) !== -1)
return true;
}
return false;
}
// noinspection JSMethodCanBeStatic
isNotOpenMessage(message) {
return message.indexOf(indexDbNotOpenMsg) !== -1;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TupleStorageIndexedDbService, deps: [{ token: TupleOfflineStorageNameService }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TupleStorageIndexedDbService });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TupleStorageIndexedDbService, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [TupleOfflineStorageNameService]
}] }]; } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVHVwbGVTdG9yYWdlSW5kZXhlZERiU2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy92b3J0ZXgvc3RvcmFnZS9UdXBsZVN0b3JhZ2VJbmRleGVkRGJTZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ25ELE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDdEMsT0FBTyxFQUVILHNCQUFzQixHQUN6QixNQUFNLDBCQUEwQixDQUFDO0FBQ2xDLE9BQU8sRUFBRSw4QkFBOEIsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBRWxGLE9BQU8sRUFDSCxvQkFBb0IsRUFDcEIsWUFBWSxFQUNaLFNBQVMsR0FDWixNQUFNLDhCQUE4QixDQUFDOztBQUV0QywrRUFBK0U7QUFFL0UsTUFBTSxXQUFXLEdBQUc7SUFDaEIsOENBQThDO0lBQzlDLG9CQUFvQjtDQUN2QixDQUFDO0FBQ0YsTUFBTSxjQUFjLEdBQUcsQ0FBQyxHQUFHLElBQUksQ0FBQztBQUVoQyxNQUFNLGlCQUFpQixHQUFHLGlEQUFpRCxDQUFDO0FBRTVFLFNBQVMsR0FBRztJQUNSLE9BQU8sSUFBSSxJQUFJLEVBQUUsQ0FBQztBQUN0QixDQUFDO0FBRUQsTUFBTSxXQUFXLEdBQUcsUUFBUSxDQUFDO0FBUTdCOzs7O0dBSUc7QUFFSCxNQUFNLE9BQU8sNEJBQTZCLFNBQVEsc0JBQXNCO0lBSVQ7SUFIM0QsRUFBRSxDQUFNO0lBQ0EscUJBQXFCLEdBQXlCLElBQUksQ0FBQztJQUUzRCxZQUEyRCxJQUFJO1FBQzNELEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUQyQyxTQUFJLEdBQUosSUFBSSxDQUFBO0lBRS9ELENBQUM7SUFFRCwrRUFBK0U7SUFDL0UsK0JBQStCO0lBQy9CLElBQUk7UUFDQSxJQUFJLElBQUksQ0FBQyxxQkFBcUIs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QUNqQixDQUFDO0lBRUQsbUNBQW1DO0lBQzNCLGdCQUFnQixDQUFDLE9BQWU7UUFDcEMsT0FBTyxPQUFPLENBQUMsT0FBTyxDQUFDLGlCQUFpQixDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7SUFDckQsQ0FBQzt3R0FqUlEsNEJBQTRCLGtCQUlqQiw4QkFBOEI7NEdBSnpDLDRCQUE0Qjs7NEZBQTVCLDRCQUE0QjtrQkFEeEMsVUFBVTs7MEJBS00sTUFBTTsyQkFBQyw4QkFBOEIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3QsIEluamVjdGFibGUgfSBmcm9tIFwiQGFuZ3VsYXIvY29yZVwiO1xuaW1wb3J0IHsgZGF0ZVN0ciB9IGZyb20gXCIuLi9VdGlsTWlzY1wiO1xuaW1wb3J0IHtcbiAgICBUdXBsZVN0b3JhZ2VCYXRjaFNhdmVBcmd1bWVudHMsXG4gICAgVHVwbGVTdG9yYWdlU2VydmljZUFCQyxcbn0gZnJvbSBcIi4vVHVwbGVTdG9yYWdlU2VydmljZUFCQ1wiO1xuaW1wb3J0IHsgVHVwbGVPZmZsaW5lU3RvcmFnZU5hbWVTZXJ2aWNlIH0gZnJvbSBcIi4vVHVwbGVPZmZsaW5lU3RvcmFnZU5hbWVTZXJ2aWNlXCI7XG5pbXBvcnQgeyBUdXBsZVNlbGVjdG9yIH0gZnJvbSBcIi4uL1R1cGxlU2VsZWN0b3JcIjtcbmltcG9ydCB7XG4gICAgYWRkSW5kZXhlZERiSGFuZGxlcnMsXG4gICAgSURCRXhjZXB0aW9uLFxuICAgIGluZGV4ZWREQixcbn0gZnJvbSBcIi4uL3N0b3JhZ2UtYXBpL2luZGV4ZWRkYi1hcGlcIjtcblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG5jb25zdCBub1NwYWNlTXNncyA9IFtcbiAgICBcInRoZXJlIHdhcyBub3QgZW5vdWdoIHJlbWFpbmluZyBzdG9yYWdlIHNwYWNlXCIsXG4gICAgXCJRdW90YUV4Y2VlZGVkRXJyb3JcIixcbl07XG5jb25zdCBub1NwYWNlUmV0cnlNcyA9IDUgKiAxMDAwO1xuXG5jb25zdCBpbmRleERiTm90T3Blbk1zZyA9IFwiSW5kZXhlZERCIHBlZWtfcGx1Z2luX2RpYWdyYW1fZ3JpZHMgaXMgbm90IG9wZW5cIjtcblxuZnVuY3Rpb24gbm93KCk6IGFueSB7XG4gICAgcmV0dXJuIG5ldyBEYXRlKCk7XG59XG5cbmNvbnN0IFRVUExFX1NUT1JFID0gXCJ0dXBsZXNcIjtcblxuaW50ZXJmYWNlIERhdGFTdHJ1Y3RJIHtcbiAgICB0dXBsZVNlbGVjdG9yOiBzdHJpbmc7XG4gICAgZGF0ZVRpbWU6IERhdGU7XG4gICAgcGF5bG9hZDogc3RyaW5nO1xufVxuXG4vKiogVHVwbGUgU3RvcmFnZSBJbmRleGVkREJcbiAqXG4gKiBUaGlzIGNsYXNzIGhhbmRsZXMgc3RvcmluZyBhbmQgcmV0cmlldmluZyB0dXBsZXMgdG8vZnJvbSBpbmRleGVkIGRiLlxuICpcbiAqL1xuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIFR1cGxlU3RvcmFnZUluZGV4ZWREYlNlcnZpY2UgZXh0ZW5kcyBUdXBsZVN0b3JhZ2VTZXJ2aWNlQUJDIHtcbiAgICBkYjogYW55O1xuICAgIHByaXZhdGUgb3BlbkluUHJvZ3Jlc3NQcm9taXNlOiBQcm9taXNlPHZvaWQ+IHwgbnVsbCA9IG51bGw7XG5cbiAgICBjb25zdHJ1Y3RvcihASW5qZWN0KFR1cGxlT2ZmbGluZVN0b3JhZ2VOYW1lU2VydmljZSkgcHVibGljIG5hbWUpIHtcbiAgICAgICAgc3VwZXIobmFtZSk7XG4gICAgfVxuXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8vIE9wZW4gdGhlIGluZGV4ZWQgZGIgZGF0YWJhc2VcbiAgICBvcGVuKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAodGhpcy5vcGVuSW5Qcm9ncmVzc1Byb21pc2UgIT0gbnVsbClcbiAgICAgICAgICAgIHJldHVybiB0aGlzLm9wZW5JblByb2dyZXNzUHJvbWlzZTtcblxuICAgICAgICB0aGlzLm9wZW5JblByb2dyZXNzUHJvbWlzZSA9IG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIC8vIERJU1AgU3RvcmVcblxuICAgICAgICAgICAgbGV0IHJlcXVlc3QgPSBpbmRleGVkREIub3Blbih0aGlzLmRiTmFtZSwgMSk7XG4gICAgICAgICAgICBhZGRJbmRleGVkRGJIYW5kbGVycyhyZXF1ZXN0LCAoKSA9PiB7XG4gICAgICAgICAgICAgICAgbGV0IG1zZyA9XG4gICAgICAgICAgICAgICAgICAgIGAke2RhdGVTdHIoKX0gSW5kZXhlZERCIDogXCIke3RoaXMuZGJOYW1lfVwiIGAgK1xuICAgICAgICAgICAgICAgICAgICBgRmFpbGVkIHRvIG9wZW4gSW5kZXhlZERCIGRhdGFiYXNlYDtcbiAgICAgICAgICAgICAgICB0aGlzLm9wZW5JblByb2dyZXNzUHJvbWlzZSA9IG51bGw7XG4gICAgICAgICAgICAgICAgcmVqZWN0KG1zZyk7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IElEQkV4Y2VwdGlvbihtc2cpO1xuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIHJlcXVlc3Qub25zdWNjZXNzID0gKGV2ZW50KSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgICAgIGAke2RhdGVTdHIoKX0gSW5kZXhlZERCIDogXCIke1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5kYk5hbWVcbiAgICAgICAgICAgICAgICAgICAgfVwiIFN1Y2Nlc3Mgb3BlbmluZyBEQmBcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLmRiID09IG51bGwpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5kYiA9IGV2ZW50LnRhcmdldC5yZXN1bHQ7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMub3BlbkluUHJvZ3Jlc3NQcm9taXNlID0gbnVsbDtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSgpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIHJlcXVlc3Qub251cGdyYWRlbmVlZGVkID0gKGV2ZW50KSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgICAgIGAke2RhdGVTdHIoKX0gSW5kZXhlZERCIDogXCIke3RoaXMuZGJOYW1lfVwiIFVwZ3JhZGluZ2BcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIGxldCBkYiA9IGV2ZW50LnRhcmdldC5yZXN1bHQ7XG5cbiAgICAgICAgICAgICAgICAvLyBTQ0hFTUEgZm9yIGRhdGFiYXNlIHBvaW50c1xuICAgICAgICAgICAgICAgIC8vIFNjaGVtYSBWZXJzaW9uIDFcbiAgICAgICAgICAgICAgICBkYi5jcmVhdGVPYmplY3RTdG9yZShUVVBMRV9TVE9SRSwgeyBrZXlQYXRoOiBcInR1cGxlU2VsZWN0b3JcIiB9KTtcblxuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgICAgICBgJHtkYXRlU3RyKCl9IEluZGV4ZWREQiA6IFwiJHt0aGlzLmRiTmFtZX1cIiBVcGdyYWRlIFN1Y2Nlc3NgXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gdGhpcy5vcGVuSW5Qcm9ncmVzc1Byb21pc2U7XG4gICAgfVxuXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8vIENoZWNrIGlmIHRoZSBEQiBpcyBvcGVuXG4gICAgYXN5bmMgaXNPcGVuKCk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgICAgICByZXR1cm4gdGhpcy5kYiAhPSBudWxsO1xuICAgIH1cblxuICAgIGFzeW5jIGNsb3NlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAoIShhd2FpdCB0aGlzLmlzT3BlbigpKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbmRleGVkREIgXCIke3RoaXMuZGJOYW1lfVwiIGlzIG5vdCBvcGVuYCk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5kYi5jbG9zZSgpO1xuICAgICAgICB0aGlzLmRiID0gbnVsbDtcbiAgICB9XG5cbiAgICBhc3luYyB0cnVuY2F0ZVN0b3JhZ2UoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGlmICghKGF3YWl0IHRoaXMuaXNPcGVuKCkpKSBhd2FpdCB0aGlzLm9wZW4oKTtcbiAgICAgICAgbGV0IHN0YXJ0VGltZSA9IG5vdygpO1xuXG4gICAgICAgIGF3YWl0IG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIGxldCByZXNwb25zZSA9IGluZGV4ZWREQi5kZWxldGVEYXRhYmFzZSh0aGlzLmRiTmFtZSk7XG5cbiAgICAgICAgICAgIGFkZEluZGV4ZWREYkhhbmRsZXJzKHJlc3BvbnNlLCAoKSA9PiB7XG4gICAgICAgICAgICAgICAgcmVqZWN0KFxuICAgICAgICAgICAgICAgICAgICBgJHtkYXRlU3RyKCl9IEluZGV4ZWREQjogdHJ1bmNhdGVTdG9yYWdlIFwidHJ1bmNhdGVTdG9yYWdlXCIgZXJyb3JgXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgSURCRXhjZXB0aW9uKFwiZGVsZXRlRGF0YWJhc2UgZXJyb3JcIik7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgcmVzcG9uc2Uub25jb21wbGV0ZSA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICBsZXQgdGltZVRha2VuID0gbm93KCkgLSBzdGFydFRpbWU7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgICAgIGAke2RhdGVTdHIoKX0gSW5kZXhlZERCOiB0cnVuY2F0ZVN0b3JhZ2VgICtcbiAgICAgICAgICAgICAgICAgICAgICAgIGAgdG9vayAke3RpbWVUYWtlbn1tcyAoaW4gdGhyZWFkKWBcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgb2JqZWN0U3RvcmVUeChmb3JXcml0ZTogYm9vbGVhbikge1xuICAgICAgICAvLyBHZXQgdGhlIFJlYWQgT25seSBjYXNlIG91dCB0aGUgd2F5LCBpdCdzIGVhc3lcbiAgICAgICAgY29uc3QgbW9kZSA9IGZvcldyaXRlID8gXCJyZWFkd3JpdGVcIiA6IFwicmVhZG9ubHlcIjtcbiAgICAgICAgY29uc3QgdHggPSB0aGlzLmRiLnRyYW5zYWN0aW9uKFRVUExFX1NUT1JFLCBtb2RlKTtcbiAgICAgICAgcmV0dXJuIHR4Lm9iamVjdFN0b3JlKFRVUExFX1NUT1JFKTtcbiAgICB9XG5cbiAgICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gICAgLy8gTG9hZCB0aGUgZGlzcGxheSBpdGVtcyBmcm9tIHRoZSBjYWNoZVxuXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8vIExvYWQgdGhlIGRpc3BsYXkgaXRlbXMgZnJvbSB0aGUgY2FjaGVcbiAgICBhc3luYyBsb2FkVHVwbGVzRW5jb2RlZChcbiAgICAgICAgdHVwbGVTZWxlY3RvcjogVHVwbGVTZWxlY3RvclxuICAgICk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD4ge1xuICAgICAgICBpZiAoIShhd2FpdCB0aGlzLmlzT3BlbigpKSkgYXdhaXQgdGhpcy5vcGVuKCk7XG4gICAgICAgIGxldCBzdGFydFRpbWU6IGFueSA9IG5vdygpO1xuXG4gICAgICAgIHJldHVybiBhd2FpdCBuZXcgUHJvbWlzZTxzdHJpbmcgfCBudWxsPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gdGhpcy5vYmplY3RTdG9yZVR4KGZhbHNlKSAvL1xuICAgICAgICAgICAgICAgIC5nZXQodHVwbGVTZWxlY3Rvci50b09yZGVyZWRKc29uU3RyKCkpO1xuXG4gICAgICAgICAgICBhZGRJbmRleGVkRGJIYW5kbGVycyhyZXF1ZXN0LCAoKSA9PiB7XG4gICAgICAgICAgICAgICAgbGV0IG1zZyA9IGAke2RhdGVTdHIoKX0gSW5kZXhlZERCOiBJbmRleCBvcGVuIGN1cnNvcmA7XG4gICAgICAgICAgICAgICAgcmVqZWN0KG1zZyk7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IElEQkV4Y2VwdGlvbihtc2cpO1xuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIHJlcXVlc3Qub25zdWNjZXNzID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgIGxldCB0aW1lVGFrZW4gPSBub3coKSAtIHN0YXJ0VGltZTtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICAgICAgYCR7ZGF0ZVN0cigpfSBJbmRleGVkREI6IGxvYWRUdXBsZXMgdG9vayAke3RpbWVUYWtlbn1tcyAoaW4gdGhyZWFkKWBcbiAgICAgICAgICAgICAgICApO1xuXG4gICAgICAgICAgICAgICAgLy8gQ2FsbGVkIGZvciBlYWNoIG1hdGNoaW5nIHJlY29yZFxuICAgICAgICAgICAgICAgIGxldCBkYXRhOiBEYXRhU3RydWN0SSB8IG51bGwgPSByZXF1ZXN0LnJlc3VsdDtcbiAgICAgICAgICAgICAgICBpZiAoZGF0YSA9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUobnVsbCk7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICByZXNvbHZlKGRhdGEucGF5bG9hZCk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBhc3luYyBiYXRjaFNhdmVUdXBsZXNFbmNvZGVkKFxuICAgICAgICBkYXRhOiBUdXBsZVN0b3JhZ2VCYXRjaFNhdmVBcmd1bWVudHNbXVxuICAgICk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAoIShhd2FpdCB0aGlzLmlzT3BlbigpKSkgYXdhaXQgdGhpcy5vcGVuKCk7XG4gICAgICAgIC8vIFRoZSBwYXlsb2FkIGlzIGEgY29udmVuaWVudCB3YXkgdG8gc2VyaWFsaXNlIGFuZCBjb21wcmVzcyB0aGUgZGF0YVxuXG4gICAgICAgIGNvbnN0IGl0ZW1zOiBEYXRhU3RydWN0SVtdID0gW107XG4gICAgICAgIGNvbnN0IG5vd0RhdGUgPSBuZXcgRGF0ZSgpO1xuXG4gICAgICAgIGZvciAoY29uc3QgZGF0YV8gb2YgZGF0YSkge1xuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGl0ZW1zLnB1c2goe1xuICAgICAgICAgICAgICAgICAgICB0dXBsZVNlbGVjdG9yOiBkYXRhXy50dXBsZVNlbGVjdG9yLnRvT3JkZXJlZEpzb25TdHIoKSxcbiAgICAgICAgICAgICAgICAgICAgZGF0ZVRpbWU6IG5vd0RhdGUsXG4gICAgICAgICAgICAgICAgICAgIHBheWxvYWQ6IGRhdGFfLnZvcnRleE1zZyxcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHN0YXJ0VGltZSA9IG5vdygpO1xuXG4gICAgICAgIHdoaWxlICh0cnVlKSB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIC8vIFJ1biB0aGUgaW5zZXJ0c1xuICAgICAgICAgICAgICAgIGNvbnN0IHN0b3JlID0gdGhpcy5vYmplY3RTdG9yZVR4KHRydWUpO1xuICAgICAgICAgICAgICAgIGNvbnN0IHByb21pc2VzID0gW107XG5cbiAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IGl0ZW0gb2YgaXRlbXMpIHtcbiAgICAgICAgICAgICAgICAgICAgcHJvbWlzZXMucHVzaChcbiAgICAgICAgICAgICAgICAgICAgICAgIG5ldyBQcm9taXNlPHZvaWQ+KChyZXMyLCByZWoyKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBzdG9yZS5wdXQoaXRlbSk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZGRJbmRleGVkRGJIYW5kbGVycyhyZXNwb25zZSwgKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWoyKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYCR7ZGF0ZVN0cigpfSBJbmRleGVkREI6IHNhdmVUdXBsZXNFbmNvZGVkIFwicHV0XCIgZXJyb3JgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBJREJFeGNlcHRpb24oXCJQdXQgZXJyb3JcIik7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNwb25zZS5vbnN1Y2Nlc3MgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHRpbWVUYWtlbiA9IG5vdygpIC0gc3RhcnRUaW1lO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGAke2RhdGVTdHIoKX0gSW5kZXhlZERCOiBzYXZlVHVwbGVzRW5jb2RlZGAgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGAgdG9vayAke3RpbWVUYWtlbn1tcyAoaW4gdGhyZWFkKWAgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGAgSW5zZXJ0ZWQvdXBkYXRlZCAke2l0ZW0ucGF5bG9hZD8ubGVuZ3RofSBvZiBlbmNvZGluZ2BcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzMigpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGF3YWl0IFByb21pc2UuYWxsKHByb21pc2VzKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuaXNOb1NwYWNlTWVzc2FnZShlKSkge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICAgICAgICAgIGAke2RhdGVTdHIoKX0gSW5kZXhlZERCOiAke3RoaXMubmFtZS5uYW1lfWAgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwiIHNhdmVUdXBsZXNFbmNvZGVkXCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwiIFdhaXRpbmcgZm9yIHNwYWNlIHF1b3RlIGV4dGVuc2lvblwiXG4gICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlPHZvaWQ+KChyKSA9PlxuICAgICAgICAgICAgICAgICAgICAgICAgc2V0VGltZW91dCgoKSA9PiByKCksIG5vU3BhY2VSZXRyeU1zKVxuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGUpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8vIEFkZCBkaXNwbHkgaXRlbXMgdG8gdGhlIGNhY2hlXG5cbiAgICBhc3luYyBzYXZlVHVwbGVzRW5jb2RlZChcbiAgICAgICAgdHVwbGVTZWxlY3RvcjogVHVwbGVTZWxlY3RvcixcbiAgICAgICAgdm9ydGV4TXNnOiBzdHJpbmdcbiAgICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgYXdhaXQgdGhpcy5iYXRjaFNhdmVUdXBsZXNFbmNvZGVkKFtcbiAgICAgICAgICAgIHsgdHVwbGVTZWxlY3RvcjogdHVwbGVTZWxlY3Rvciwgdm9ydGV4TXNnOiB2b3J0ZXhNc2cgfSxcbiAgICAgICAgXSk7XG4gICAgfVxuXG4gICAgYXN5bmMgZGVsZXRlVHVwbGVzKHR1cGxlU2VsZWN0b3I6IFR1cGxlU2VsZWN0b3IpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgaWYgKCEoYXdhaXQgdGhpcy5pc09wZW4oKSkpIGF3YWl0IHRoaXMub3BlbigpO1xuICAgICAgICAvLyBUaGUgcGF5bG9hZCBpcyBhIGNvbnZlbmllbnQgd2F5IHRvIHNlcmlhbGlzZSBhbmQgY29tcHJlc3MgdGhlIGRhdGFcbiAgICAgICAgY29uc3QgdHVwbGVTZWxlY3RvclN0ciA9IHR1cGxlU2VsZWN0b3IudG9PcmRlcmVkSnNvblN0cigpO1xuXG4gICAgICAgIGNvbnN0IHN0YXJ0VGltZSA9IG5vdygpO1xuXG4gICAgICAgIGF3YWl0IG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIC8vIFJ1biB0aGUgaW5zZXJ0c1xuICAgICAgICAgICAgY29uc3QgcmVzcG9uc2UgPSB0aGlzLm9iamVjdFN0b3JlVHgodHJ1ZSkgLy9cbiAgICAgICAgICAgICAgICAuZGVsZXRlKHR1cGxlU2VsZWN0b3JTdHIpO1xuXG4gICAgICAgICAgICBhZGRJbmRleGVkRGJIYW5kbGVycyhyZXNwb25zZSwgKCkgPT4ge1xuICAgICAgICAgICAgICAgIHJlamVjdChgJHtkYXRlU3RyKCl9IEluZGV4ZWREQjogZGVsZXRlVHVwbGVzIFwiZGVsZXRlXCIgZXJyb3JgKTtcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgSURCRXhjZXB0aW9uKFwiUHV0IGVycm9yXCIpO1xuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIHJlc3BvbnNlLm9uc3VjY2VzcyA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICBsZXQgdGltZVRha2VuID0gbm93KCkgLSBzdGFydFRpbWU7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgICAgIGAke2RhdGVTdHIoKX0gSW5kZXhlZERCOiBkZWxldGVUdXBsZXNgICtcbiAgICAgICAgICAgICAgICAgICAgICAgIGAgdG9vayAke3RpbWVUYWtlbn1tcyAoaW4gdGhyZWFkKWBcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIGFzeW5jIGRlbGV0ZU9sZFR1cGxlcyhkZWxldGVEYXRhQmVmb3JlRGF0ZTogRGF0ZSk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAoIShhd2FpdCB0aGlzLmlzT3BlbigpKSkgYXdhaXQgdGhpcy5vcGVuKCk7XG4gICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgYCR7ZGF0ZVN0cigpfSBXQVJOSU5HOiBkZWxldGVPbGRUdXBsZSBub3QgaW1wbGVtZW50ZWQgZm9yIEluZGV4ZWREQmBcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xuICAgIH1cblxuICAgIC8vIG5vaW5zcGVjdGlvbiBKU01ldGhvZENhbkJlU3RhdGljXG4gICAgcHJpdmF0ZSBpc05vU3BhY2VNZXNzYWdlKG1lc3NhZ2U6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgICAgICBmb3IgKGNvbnN0IG1zZyBvZiBub1NwYWNlTXNncykge1xuICAgICAgICAgICAgaWYgKG1lc3NhZ2UuaW5kZXhPZihtc2cpICE9PSAtMSkgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIC8vIG5vaW5zcGVjdGlvbiBKU01ldGhvZENhbkJlU3RhdGljXG4gICAgcHJpdmF0ZSBpc05vdE9wZW5NZXNzYWdlKG1lc3NhZ2U6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gbWVzc2FnZS5pbmRleE9mKGluZGV4RGJOb3RPcGVuTXNnKSAhPT0gLTE7XG4gICAgfVxufVxuIl19