UNPKG

@synerty/vortexjs

Version:

Custom observable data serialisation and routing based on Angular 2+

238 lines 34.6 kB
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