@synerty/vortexjs
Version:
Custom observable data serialisation and routing based on Angular 2+
233 lines • 33.7 kB
JavaScript
import { Injectable } from "@angular/core";
import { dateStr } from "../UtilMisc";
import { Payload } from "../Payload";
import { TupleActionStorageServiceABC } from "./TupleActionStorageServiceABC";
import { addIndexedDbHandlers, IDBException, indexedDB, } from "../storage-api/indexeddb-api";
import * as i0 from "@angular/core";
// ----------------------------------------------------------------------------
function now() {
return new Date();
}
const DB_NAME = "tupleActions";
const ACTION_STORE = "tupleActions";
const ACTION_KEY_PATH = "scopeUuid";
/** Tuple Storage IndexedDB
*
* This class handles storing and retrieving tuples to/from indexed db.
*
*/
export class TupleActionStorageIndexedDbService extends TupleActionStorageServiceABC {
db;
openInProgressPromise = null;
RETRIES = 5;
RETRY_DELAY_MS = 100;
constructor() {
super();
}
async storeAction(scope, tupleAction, payload) {
await this.open();
let startTime = now();
const vortexMsg = await payload.toEncodedPayload();
const item = {
scope: scope,
scopeUuid: `${scope}|${tupleAction.uuid}`,
encodedPayload: vortexMsg,
};
let timeTaken = now() - startTime;
console.log(`${dateStr()} IndexedDB: toVortexMsg took ${timeTaken}ms `);
startTime = now();
await new Promise(async (resolve, reject) => {
try {
const tx = await this.transaction(true);
let store = tx.objectStore(ACTION_STORE);
let response = store.put(item);
addIndexedDbHandlers(response, () => {
tx.abort();
reject(`${dateStr()} IndexedDB: saveTuples "put" error`);
throw new IDBException("Put error");
});
response.onsuccess = () => {
tx.oncomplete = () => {
let timeTaken = now() - startTime;
console.log(`${dateStr()} IndexedDB: storeAction` +
` took ${timeTaken}ms (in thread)`);
resolve();
};
tx.onerror = () => {
reject(`${dateStr()} IndexedDB: Transaction failed to commit`);
};
};
}
catch (e) {
reject(`${dateStr()} IndexedDB: Transaction failed: ${e}`);
}
});
}
async loadNextAction() {
await this.open();
const encodedPayload = await new Promise(async (resolve, reject) => {
try {
const tx = await this.transaction(false); // Changed to readonly
const store = tx.objectStore(ACTION_STORE);
const response = store.openCursor();
addIndexedDbHandlers(response, () => {
tx.abort();
reject(`${dateStr()} IndexedDB: loadNextAction cursor error`);
throw new IDBException("Cursor error");
});
response.onsuccess = (ev) => {
const cursor = response.result || ev.target.result;
if (!cursor) {
resolve(null);
return;
}
resolve(cursor.value.encodedPayload);
tx.abort();
};
tx.onerror = () => {
reject(`${dateStr()} IndexedDB: Transaction failed`);
};
}
catch (e) {
reject(`${dateStr()} IndexedDB: Transaction failed: ${e}`);
}
});
if (encodedPayload == null)
return null;
return await Payload.fromEncodedPayload(encodedPayload);
}
async countActions() {
await this.open();
return new Promise(async (resolve, reject) => {
try {
const tx = await this.transaction(false);
const response = tx.objectStore(ACTION_STORE).count();
addIndexedDbHandlers(response, () => {
tx.abort();
reject(`${dateStr()} IndexedDB: countActions error`);
throw new IDBException("Count error");
});
response.onsuccess = () => {
resolve(response.result);
};
tx.onerror = () => {
reject(`${dateStr()} IndexedDB: Transaction failed`);
};
}
catch (e) {
reject(`${dateStr()} IndexedDB: Transaction failed: ${e}`);
}
});
}
async deleteAction(scope, actionUuid) {
await this.open();
const scopeUuid = `${scope}|${actionUuid}`;
await new Promise(async (resolve, reject) => {
try {
const tx = await this.transaction(true);
const response = tx.objectStore(ACTION_STORE).delete(scopeUuid);
addIndexedDbHandlers(response, () => {
tx.abort();
reject(`${dateStr()} IndexedDB: deleteAction error`);
throw new IDBException("Delete error");
});
response.onsuccess = () => {
tx.oncomplete = () => resolve();
tx.onerror = () => {
reject(`${dateStr()} IndexedDB: Transaction failed to commit`);
};
};
}
catch (e) {
reject(`${dateStr()} IndexedDB: Transaction failed: ${e}`);
}
});
}
// ----------------------------------------------------------------------------
// Open the indexed db database
open() {
if (this.isOpen())
return Promise.resolve();
if (this.openInProgressPromise != null)
return this.openInProgressPromise;
this.openInProgressPromise = new Promise((resolve, reject) => {
let request = indexedDB.open(DB_NAME, 1);
addIndexedDbHandlers(request, () => {
let msg = `${dateStr()} IndexedDB : "${DB_NAME}" Failed to open IndexedDB database`;
this.openInProgressPromise = null;
reject(msg);
throw new IDBException(msg);
});
request.onsuccess = (event) => {
console.log(`${dateStr()} IndexedDB : "${DB_NAME}" Success opening DB`);
if (this.db == null) {
this.db = event.target.result;
this.openInProgressPromise = null;
resolve();
}
};
request.onupgradeneeded = (event) => {
console.log(`${dateStr()} IndexedDB : "${DB_NAME}" Upgrading`);
let db = event.target.result;
// SCHEMA for database points
// Schema Version 1
db.createObjectStore(ACTION_STORE, {
keyPath: ACTION_KEY_PATH,
});
console.log(`${dateStr()} IndexedDB : "${DB_NAME}" Upgrade Success`);
};
});
return this.openInProgressPromise;
}
// ----------------------------------------------------------------------------
// Check if the DB is open
isOpen() {
return this.db != null;
}
close() {
if (!this.isOpen()) {
throw new Error(`IndexedDB "${DB_NAME}" is not open`);
}
this.db.close();
this.db = null;
}
async delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async transaction(forWrite) {
let retries = this.RETRIES;
while (true) {
try {
// Get the Read Only case out the way, it's easy
return this.db.transaction(ACTION_STORE, forWrite ? "readwrite" : "readonly");
}
catch (e) {
retries -= 1;
if (!retries) {
const msg = `ERROR: Failed to create transaction after ${this.RETRIES} attempts: ${e.toString()}`;
console.log(msg);
throw new Error(msg);
}
if (e.toString().indexOf("The database is closing") !== -1) {
try {
this.close();
// Add delay before reopening to allow cleanup
await this.delay(this.RETRY_DELAY_MS);
await this.open();
console.log(`${dateStr()} IndexedDB: Reopened database after closure`);
}
catch (reopenError) {
console.error(`${dateStr()} IndexedDB: Failed to reopen database:`, reopenError);
throw reopenError;
}
}
}
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TupleActionStorageIndexedDbService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TupleActionStorageIndexedDbService });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: TupleActionStorageIndexedDbService, decorators: [{
type: Injectable
}], ctorParameters: function () { return []; } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVHVwbGVBY3Rpb25TdG9yYWdlSW5kZXhlZERiU2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy92b3J0ZXgvYWN0aW9uLXN0b3JhZ2UvVHVwbGVBY3Rpb25TdG9yYWdlSW5kZXhlZERiU2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzNDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDdEMsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLFlBQVksQ0FBQztBQUNyQyxPQUFPLEVBQUUsNEJBQTRCLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUM5RSxPQUFPLEVBQ0gsb0JBQW9CLEVBQ3BCLFlBQVksRUFDWixTQUFTLEdBQ1osTUFBTSw4QkFBOEIsQ0FBQzs7QUFHdEMsK0VBQStFO0FBRS9FLFNBQVMsR0FBRztJQUNSLE9BQU8sSUFBSSxJQUFJLEVBQUUsQ0FBQztBQUN0QixDQUFDO0FBRUQsTUFBTSxPQUFPLEdBQUcsY0FBYyxDQUFDO0FBQy9CLE1BQU0sWUFBWSxHQUFHLGNBQWMsQ0FBQztBQUNwQyxNQUFNLGVBQWUsR0FBRyxXQUFXLENBQUM7QUFRcEM7Ozs7R0FJRztBQUVILE1BQU0sT0FBTyxrQ0FBbUMsU0FBUSw0QkFBNEI7SUFDeEUsRUFBRSxDQUFNO0lBQ1IscUJBQXFCLEdBQXlCLElBQUksQ0FBQztJQUMxQyxPQUFPLEdBQUcsQ0FBQyxDQUFDO0lBQ1osY0FBYyxHQUFHLEdBQUcsQ0FBQztJQUV0QztRQUNJLEtBQUssRUFBRSxDQUFDO0lBQ1osQ0FBQztJQUVELEtBQUssQ0FBQyxXQUFXLENBQ2IsS0FBYSxFQUNiLFdBQTJCLEVBQzNCLE9BQWdCO1FBRWhCLE1BQU0sSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ2xCLElBQUksU0FBUyxHQUFHLEdBQUcsRUFBRSxDQUFDO1FBRXRCLE1B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QUksQ0FBQyxJQUFJLEVBQUUsQ0FBQzt3QkFDbEIsT0FBTyxDQUFDLEdBQUcsQ0FDUCxHQUFHLE9BQU8sRUFBRSw2Q0FBNkMsQ0FDNUQsQ0FBQztxQkFDTDtvQkFBQyxPQUFPLFdBQVcsRUFBRTt3QkFDbEIsT0FBTyxDQUFDLEtBQUssQ0FDVCxHQUFHLE9BQU8sRUFBRSx3Q0FBd0MsRUFDcEQsV0FBVyxDQUNkLENBQUM7d0JBQ0YsTUFBTSxXQUFXLENBQUM7cUJBQ3JCO2lCQUNKO2FBQ0o7U0FDSjtJQUNMLENBQUM7d0dBclFRLGtDQUFrQzs0R0FBbEMsa0NBQWtDOzs0RkFBbEMsa0NBQWtDO2tCQUQ5QyxVQUFVIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSB9IGZyb20gXCJAYW5ndWxhci9jb3JlXCI7XG5pbXBvcnQgeyBkYXRlU3RyIH0gZnJvbSBcIi4uL1V0aWxNaXNjXCI7XG5pbXBvcnQgeyBQYXlsb2FkIH0gZnJvbSBcIi4uL1BheWxvYWRcIjtcbmltcG9ydCB7IFR1cGxlQWN0aW9uU3RvcmFnZVNlcnZpY2VBQkMgfSBmcm9tIFwiLi9UdXBsZUFjdGlvblN0b3JhZ2VTZXJ2aWNlQUJDXCI7XG5pbXBvcnQge1xuICAgIGFkZEluZGV4ZWREYkhhbmRsZXJzLFxuICAgIElEQkV4Y2VwdGlvbixcbiAgICBpbmRleGVkREIsXG59IGZyb20gXCIuLi9zdG9yYWdlLWFwaS9pbmRleGVkZGItYXBpXCI7XG5pbXBvcnQgeyBUdXBsZUFjdGlvbkFCQyB9IGZyb20gXCIuLi9UdXBsZUFjdGlvblwiO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbmZ1bmN0aW9uIG5vdygpOiBhbnkge1xuICAgIHJldHVybiBuZXcgRGF0ZSgpO1xufVxuXG5jb25zdCBEQl9OQU1FID0gXCJ0dXBsZUFjdGlvbnNcIjtcbmNvbnN0IEFDVElPTl9TVE9SRSA9IFwidHVwbGVBY3Rpb25zXCI7XG5jb25zdCBBQ1RJT05fS0VZX1BBVEggPSBcInNjb3BlVXVpZFwiO1xuXG5pbnRlcmZhY2UgVHVwbGVBY3Rpb25TdG9yYWdlU3RydWN0SSB7XG4gICAgc2NvcGU6IHN0cmluZztcbiAgICBzY29wZVV1aWQ6IHN0cmluZztcbiAgICBlbmNvZGVkUGF5bG9hZDogc3RyaW5nO1xufVxuXG4vKiogVHVwbGUgU3RvcmFnZSBJbmRleGVkREJcbiAqXG4gKiBUaGlzIGNsYXNzIGhhbmRsZXMgc3RvcmluZyBhbmQgcmV0cmlldmluZyB0dXBsZXMgdG8vZnJvbSBpbmRleGVkIGRiLlxuICpcbiAqL1xuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIFR1cGxlQWN0aW9uU3RvcmFnZUluZGV4ZWREYlNlcnZpY2UgZXh0ZW5kcyBUdXBsZUFjdGlvblN0b3JhZ2VTZXJ2aWNlQUJDIHtcbiAgICBwcml2YXRlIGRiOiBhbnk7XG4gICAgcHJpdmF0ZSBvcGVuSW5Qcm9ncmVzc1Byb21pc2U6IFByb21pc2U8dm9pZD4gfCBudWxsID0gbnVsbDtcbiAgICBwcml2YXRlIHJlYWRvbmx5IFJFVFJJRVMgPSA1O1xuICAgIHByaXZhdGUgcmVhZG9ubHkgUkVUUllfREVMQVlfTVMgPSAxMDA7XG5cbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgc3VwZXIoKTtcbiAgICB9XG5cbiAgICBhc3luYyBzdG9yZUFjdGlvbihcbiAgICAgICAgc2NvcGU6IHN0cmluZyxcbiAgICAgICAgdHVwbGVBY3Rpb246IFR1cGxlQWN0aW9uQUJDLFxuICAgICAgICBwYXlsb2FkOiBQYXlsb2FkLFxuICAgICk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCB0aGlzLm9wZW4oKTtcbiAgICAgICAgbGV0IHN0YXJ0VGltZSA9IG5vdygpO1xuXG4gICAgICAgIGNvbnN0IHZvcnRleE1zZyA9IGF3YWl0IHBheWxvYWQudG9FbmNvZGVkUGF5bG9hZCgpO1xuXG4gICAgICAgIGNvbnN0IGl0ZW06IFR1cGxlQWN0aW9uU3RvcmFnZVN0cnVjdEkgPSB7XG4gICAgICAgICAgICBzY29wZTogc2NvcGUsXG4gICAgICAgICAgICBzY29wZVV1aWQ6IGAke3Njb3BlfXwke3R1cGxlQWN0aW9uLnV1aWR9YCxcbiAgICAgICAgICAgIGVuY29kZWRQYXlsb2FkOiB2b3J0ZXhNc2csXG4gICAgICAgIH07XG5cbiAgICAgICAgbGV0IHRpbWVUYWtlbiA9IG5vdygpIC0gc3RhcnRUaW1lO1xuICAgICAgICBjb25zb2xlLmxvZyhgJHtkYXRlU3RyKCl9IEluZGV4ZWREQjogdG9Wb3J0ZXhNc2cgdG9vayAke3RpbWVUYWtlbn1tcyBgKTtcblxuICAgICAgICBzdGFydFRpbWUgPSBub3coKTtcblxuICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZTx2b2lkPihhc3luYyAocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHR4ID0gYXdhaXQgdGhpcy50cmFuc2FjdGlvbih0cnVlKTtcbiAgICAgICAgICAgICAgICBsZXQgc3RvcmUgPSB0eC5vYmplY3RTdG9yZShBQ1RJT05fU1RPUkUpO1xuICAgICAgICAgICAgICAgIGxldCByZXNwb25zZSA9IHN0b3JlLnB1dChpdGVtKTtcblxuICAgICAgICAgICAgICAgIGFkZEluZGV4ZWREYkhhbmRsZXJzKHJlc3BvbnNlLCAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHR4LmFib3J0KCk7XG4gICAgICAgICAgICAgICAgICAgIHJlamVjdChgJHtkYXRlU3RyKCl9IEluZGV4ZWREQjogc2F2ZVR1cGxlcyBcInB1dFwiIGVycm9yYCk7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBJREJFeGNlcHRpb24oXCJQdXQgZXJyb3JcIik7XG4gICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICByZXNwb25zZS5vbnN1Y2Nlc3MgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHR4Lm9uY29tcGxldGUgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBsZXQgdGltZVRha2VuID0gbm93KCkgLSBzdGFydFRpbWU7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBgJHtkYXRlU3RyKCl9IEluZGV4ZWREQjogc3RvcmVBY3Rpb25gICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYCB0b29rICR7dGltZVRha2VufW1zIChpbiB0aHJlYWQpYCxcbiAgICAgICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlKCk7XG4gICAgICAgICAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgICAgICAgICAgdHgub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlamVjdChcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBgJHtkYXRlU3RyKCl9IEluZGV4ZWREQjogVHJhbnNhY3Rpb24gZmFpbGVkIHRvIGNvbW1pdGAsXG4gICAgICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgcmVqZWN0KGAke2RhdGVTdHIoKX0gSW5kZXhlZERCOiBUcmFuc2FjdGlvbiBmYWlsZWQ6ICR7ZX1gKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgYXN5bmMgbG9hZE5leHRBY3Rpb24oKTogUHJvbWlzZTxQYXlsb2FkIHwgbnVsbD4ge1xuICAgICAgICBhd2FpdCB0aGlzLm9wZW4oKTtcbiAgICAgICAgY29uc3QgZW5jb2RlZFBheWxvYWQgPSBhd2FpdCBuZXcgUHJvbWlzZTxzdHJpbmc+KFxuICAgICAgICAgICAgYXN5bmMgKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHR4ID0gYXdhaXQgdGhpcy50cmFuc2FjdGlvbihmYWxzZSk7IC8vIENoYW5nZWQgdG8gcmVhZG9ubHlcbiAgICAgICAgICAgICAgICAgICAgY29uc3Qgc3RvcmUgPSB0eC5vYmplY3RTdG9yZShBQ1RJT05fU1RPUkUpO1xuICAgICAgICAgICAgICAgICAgICBjb25zdCByZXNwb25zZSA9IHN0b3JlLm9wZW5DdXJzb3IoKTtcblxuICAgICAgICAgICAgICAgICAgICBhZGRJbmRleGVkRGJIYW5kbGVycyhyZXNwb25zZSwgKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgdHguYWJvcnQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlamVjdChcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBgJHtkYXRlU3RyKCl9IEluZGV4ZWREQjogbG9hZE5leHRBY3Rpb24gY3Vyc29yIGVycm9yYCxcbiAgICAgICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgSURCRXhjZXB0aW9uKFwiQ3Vyc29yIGVycm9yXCIpO1xuICAgICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgICAgICByZXNwb25zZS5vbnN1Y2Nlc3MgPSAoZXYpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGN1cnNvciA9IHJlc3BvbnNlLnJlc3VsdCB8fCBldi50YXJnZXQucmVzdWx0O1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFjdXJzb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlKG51bGwpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdmUoY3Vyc29yLnZhbHVlLmVuY29kZWRQYXlsb2FkKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHR4LmFib3J0KCk7XG4gICAgICAgICAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgICAgICAgICAgdHgub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlamVjdChgJHtkYXRlU3RyKCl9IEluZGV4ZWREQjogVHJhbnNhY3Rpb24gZmFpbGVkYCk7XG4gICAgICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgICAgICByZWplY3QoYCR7ZGF0ZVN0cigpfSBJbmRleGVkREI6IFRyYW5zYWN0aW9uIGZhaWxlZDogJHtlfWApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICk7XG5cbiAgICAgICAgaWYgKGVuY29kZWRQYXlsb2FkID09IG51bGwpIHJldHVybiBudWxsO1xuICAgICAgICByZXR1cm4gYXdhaXQgUGF5bG9hZC5mcm9tRW5jb2RlZFBheWxvYWQoZW5jb2RlZFBheWxvYWQpO1xuICAgIH1cblxuICAgIGFzeW5jIGNvdW50QWN0aW9ucygpOiBQcm9taXNlPG51bWJlcj4ge1xuICAgICAgICBhd2FpdCB0aGlzLm9wZW4oKTtcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlPG51bWJlcj4oYXN5bmMgKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBjb25zdCB0eCA9IGF3YWl0IHRoaXMudHJhbnNhY3Rpb24oZmFsc2UpO1xuICAgICAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gdHgub2JqZWN0U3RvcmUoQUNUSU9OX1NUT1JFKS5jb3VudCgpO1xuXG4gICAgICAgICAgICAgICAgYWRkSW5kZXhlZERiSGFuZGxlcnMocmVzcG9uc2UsICgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdHguYWJvcnQoKTtcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KGAke2RhdGVTdHIoKX0gSW5kZXhlZERCOiBjb3VudEFjdGlvbnMgZXJyb3JgKTtcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgbmV3IElEQkV4Y2VwdGlvbihcIkNvdW50IGVycm9yXCIpO1xuICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgcmVzcG9uc2Uub25zdWNjZXNzID0gKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHJlc3BvbnNlLnJlc3VsdCk7XG4gICAgICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgICAgIHR4Lm9uZXJyb3IgPSAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHJlamVjdChgJHtkYXRlU3RyKCl9IEluZGV4ZWREQjogVHJhbnNhY3Rpb24gZmFpbGVkYCk7XG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgICAgICByZWplY3QoYCR7ZGF0ZVN0cigpfSBJbmRleGVkREI6IFRyYW5zYWN0aW9uIGZhaWxlZDogJHtlfWApO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBhc3luYyBkZWxldGVBY3Rpb24oc2NvcGU6IHN0cmluZywgYWN0aW9uVXVpZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGF3YWl0IHRoaXMub3BlbigpO1xuICAgICAgICBjb25zdCBzY29wZVV1aWQgPSBgJHtzY29wZX18JHthY3Rpb25VdWlkfWA7XG5cbiAgICAgICAgYXdhaXQgbmV3IFByb21pc2U8dm9pZD4oYXN5bmMgKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBjb25zdCB0eCA9IGF3YWl0IHRoaXMudHJhbnNhY3Rpb24odHJ1ZSk7XG4gICAgICAgICAgICAgICAgY29uc3QgcmVzcG9uc2UgPSB0eC5vYmplY3RTdG9yZShBQ1RJT05fU1RPUkUpLmRlbGV0ZShzY29wZVV1aWQpO1xuXG4gICAgICAgICAgICAgICAgYWRkSW5kZXhlZERiSGFuZGxlcnMocmVzcG9uc2UsICgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdHguYWJvcnQoKTtcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KGAke2RhdGVTdHIoKX0gSW5kZXhlZERCOiBkZWxldGVBY3Rpb24gZXJyb3JgKTtcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgbmV3IElEQkV4Y2VwdGlvbihcIkRlbGV0ZSBlcnJvclwiKTtcbiAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgIHJlc3BvbnNlLm9uc3VjY2VzcyA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdHgub25jb21wbGV0ZSA9ICgpID0+IHJlc29sdmUoKTtcbiAgICAgICAgICAgICAgICAgICAgdHgub25lcnJvciA9ICgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlamVjdChcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBgJHtkYXRlU3RyKCl9IEluZGV4ZWREQjogVHJhbnNhY3Rpb24gZmFpbGVkIHRvIGNvbW1pdGAsXG4gICAgICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgcmVqZWN0KGAke2RhdGVTdHIoKX0gSW5kZXhlZERCOiBUcmFuc2FjdGlvbiBmYWlsZWQ6ICR7ZX1gKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAgIC8vIE9wZW4gdGhlIGluZGV4ZWQgZGIgZGF0YWJhc2VcbiAgICBvcGVuKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAodGhpcy5pc09wZW4oKSkgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xuXG4gICAgICAgIGlmICh0aGlzLm9wZW5JblByb2dyZXNzUHJvbWlzZSAhPSBudWxsKVxuICAgICAgICAgICAgcmV0dXJuIHRoaXMub3BlbkluUHJvZ3Jlc3NQcm9taXNlO1xuXG4gICAgICAgIHRoaXMub3BlbkluUHJvZ3Jlc3NQcm9taXNlID0gbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgbGV0IHJlcXVlc3QgPSBpbmRleGVkREIub3BlbihEQl9OQU1FLCAxKTtcblxuICAgICAgICAgICAgYWRkSW5kZXhlZERiSGFuZGxlcnMocmVxdWVzdCwgKCkgPT4ge1xuICAgICAgICAgICAgICAgIGxldCBtc2cgPSBgJHtkYXRlU3RyKCl9IEluZGV4ZWREQiA6IFwiJHtEQl9OQU1FfVwiIEZhaWxlZCB0byBvcGVuIEluZGV4ZWREQiBkYXRhYmFzZWA7XG4gICAgICAgICAgICAgICAgdGhpcy5vcGVuSW5Qcm9ncmVzc1Byb21pc2UgPSBudWxsO1xuICAgICAgICAgICAgICAgIHJlamVjdChtc2cpO1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBJREJFeGNlcHRpb24obXNnKTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICByZXF1ZXN0Lm9uc3VjY2VzcyA9IChldmVudCkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgICAgICBgJHtkYXRlU3RyKCl9IEluZGV4ZWREQiA6IFwiJHtEQl9OQU1FfVwiIFN1Y2Nlc3Mgb3BlbmluZyBEQmAsXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5kYiA9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZGIgPSBldmVudC50YXJnZXQucmVzdWx0O1xuICAgICAgICAgICAgICAgICAgICB0aGlzLm9wZW5JblByb2dyZXNzUHJvbWlzZSA9IG51bGw7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICByZXF1ZXN0Lm9udXBncmFkZW5lZWRlZCA9IChldmVudCkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGAke2RhdGVTdHIoKX0gSW5kZXhlZERCIDogXCIke0RCX05BTUV9XCIgVXBncmFkaW5nYCk7XG4gICAgICAgICAgICAgICAgbGV0IGRiID0gZXZlbnQudGFyZ2V0LnJlc3VsdDtcblxuICAgICAgICAgICAgICAgIC8vIFNDSEVNQSBmb3IgZGF0YWJhc2UgcG9pbnRzXG4gICAgICAgICAgICAgICAgLy8gU2NoZW1hIFZlcnNpb24gMVxuICAgICAgICAgICAgICAgIGRiLmNyZWF0ZU9iamVjdFN0b3JlKEFDVElPTl9TVE9SRSwge1xuICAgICAgICAgICAgICAgICAgICBrZXlQYXRoOiBBQ1RJT05fS0VZX1BBVEgsXG4gICAgICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICAgICAgYCR7ZGF0ZVN0cigpfSBJbmRleGVkREIgOiBcIiR7REJfTkFNRX1cIiBVcGdyYWRlIFN1Y2Nlc3NgLFxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9O1xuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIHRoaXMub3BlbkluUHJvZ3Jlc3NQcm9taXNlO1xuICAgIH1cblxuICAgIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgICAvLyBDaGVjayBpZiB0aGUgREIgaXMgb3BlblxuICAgIGlzT3BlbigpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZGIgIT0gbnVsbDtcbiAgICB9XG5cbiAgICBjbG9zZSgpOiB2b2lkIHtcbiAgICAgICAgaWYgKCF0aGlzLmlzT3BlbigpKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEluZGV4ZWREQiBcIiR7REJfTkFNRX1cIiBpcyBub3Qgb3BlbmApO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuZGIuY2xvc2UoKTtcbiAgICAgICAgdGhpcy5kYiA9IG51bGw7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBkZWxheShtczogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gc2V0VGltZW91dChyZXNvbHZlLCBtcykpO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgdHJhbnNhY3Rpb24oZm9yV3JpdGU6IGJvb2xlYW4pOiBQcm9taXNlPGFueT4ge1xuICAgICAgICBsZXQgcmV0cmllcyA9IHRoaXMuUkVUUklFUztcbiAgICAgICAgd2hpbGUgKHRydWUpIHtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgLy8gR2V0IHRoZSBSZWFkIE9ubHkgY2FzZSBvdXQgdGhlIHdheSwgaXQncyBlYXN5XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuZGIudHJhbnNhY3Rpb24oXG4gICAgICAgICAgICAgICAgICAgIEFDVElPTl9TVE9SRSxcbiAgICAgICAgICAgICAgICAgICAgZm9yV3JpdGUgPyBcInJlYWR3cml0ZVwiIDogXCJyZWFkb25seVwiLFxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgcmV0cmllcyAtPSAxO1xuICAgICAgICAgICAgICAgIGlmICghcmV0cmllcykge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBtc2cgPSBgRVJST1I6IEZhaWxlZCB0byBjcmVhdGUgdHJhbnNhY3Rpb24gYWZ0ZXIgJHt0aGlzLlJFVFJJRVN9IGF0dGVtcHRzOiAke2UudG9TdHJpbmcoKX1gO1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhtc2cpO1xuICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IobXNnKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAoZS50b1N0cmluZygpLmluZGV4T2YoXCJUaGUgZGF0YWJhc2UgaXMgY2xvc2luZ1wiKSAhPT0gLTEpIHtcbiAgICAgICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY2xvc2UoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIEFkZCBkZWxheSBiZWZvcmUgcmVvcGVuaW5nIHRvIGFsbG93IGNsZWFudXBcbiAgICAgICAgICAgICAgICAgICAgICAgIGF3YWl0IHRoaXMuZGVsYXkodGhpcy5SRVRSWV9ERUxBWV9NUyk7XG4gICAgICAgICAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLm9wZW4oKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGAke2RhdGVTdHIoKX0gSW5kZXhlZERCOiBSZW9wZW5lZCBkYXRhYmFzZSBhZnRlciBjbG9zdXJlYCxcbiAgICAgICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgIH0gY2F0Y2ggKHJlb3BlbkVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmVycm9yKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGAke2RhdGVTdHIoKX0gSW5kZXhlZERCOiBGYWlsZWQgdG8gcmVvcGVuIGRhdGFiYXNlOmAsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVvcGVuRXJyb3IsXG4gICAgICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhyb3cgcmVvcGVuRXJyb3I7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG59XG4iXX0=