@decaf-ts/for-postgres
Version:
template for ts projects
187 lines • 20.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PostgresDispatch = void 0;
const core_1 = require("@decaf-ts/core");
const db_decorators_1 = require("@decaf-ts/db-decorators");
/**
* @description Dispatcher for PostgreSQL database change events
* @summary Handles the subscription to and processing of database change events from a PostgreSQL database,
* notifying observers when records are created, updated, or deleted
* @template Pool - The pg Pool type
* @param {number} [timeout=5000] - Timeout in milliseconds for notification requests
* @class PostgresDispatch
* @example
* ```typescript
* // Create a dispatcher for a PostgreSQL database
* const pool = new Pool({
* user: 'postgres',
* password: 'password',
* host: 'localhost',
* port: 5432,
* database: 'mydb'
* });
* const adapter = new PostgreSQLAdapterImpl(pool);
* const dispatch = new PostgreSQLDispatch();
*
* // The dispatcher will automatically subscribe to notifications
* // and notify observers when records change
* ```
* @mermaid
* classDiagram
* class Dispatch {
* +initialize()
* +updateObservers()
* }
* class PostgreSQLDispatch {
* -observerLastUpdate?: string
* -attemptCounter: number
* -timeout: number
* -client?: PoolClient
* +constructor(timeout)
* #notificationHandler()
* #initialize()
* }
* Dispatch <|-- PostgreSQLDispatch
*/
class PostgresDispatch extends core_1.Dispatch {
constructor(timeout = 5000) {
super();
this.timeout = timeout;
this.attemptCounter = 0;
}
/**
* @description Processes database notification events
* @summary Handles the notifications from PostgreSQL LISTEN/NOTIFY mechanism,
* and notifies observers about record changes
* @param {Notification} notification - The notification from PostgreSQL
* @return {Promise<void>} A promise that resolves when all notifications have been processed
* @mermaid
* sequenceDiagram
* participant D as PostgreSQLDispatch
* participant L as Logger
* participant O as Observers
* Note over D: Receive notification from PostgreSQL
* D->>D: Parse notification payload
* D->>D: Extract table, operation, and ids
* D->>O: updateObservers(table, operation, ids)
* D->>D: Update observerLastUpdate
* D->>L: Log successful dispatch
*/
async notificationHandler(notification) {
const log = this.log.for(this.notificationHandler);
try {
// Parse the notification payload (expected format: table:operation:id1,id2,...)
const payload = notification.payload;
const [table, operation, idsString] = payload.split(":");
const ids = idsString.split(",");
if (!table || !operation || !ids.length) {
return log.error(`Invalid notification format: ${payload}`);
}
// Map operation string to OperationKeys
let operationKey;
switch (operation.toLowerCase()) {
case "insert":
operationKey = db_decorators_1.OperationKeys.CREATE;
break;
case "update":
operationKey = db_decorators_1.OperationKeys.UPDATE;
break;
case "delete":
operationKey = db_decorators_1.OperationKeys.DELETE;
break;
default:
return log.error(`Unknown operation: ${operation}`);
}
// Notify observers
await this.updateObservers(table, operationKey, ids);
this.observerLastUpdate = new Date().toISOString();
log.verbose(`Observer refresh dispatched by ${operation} for ${table}`);
log.debug(`pks: ${ids}`);
}
catch (e) {
log.error(`Failed to process notification: ${e}`);
}
}
/**
* @description Initializes the dispatcher and subscribes to database notifications
* @summary Sets up the LISTEN mechanism to subscribe to PostgreSQL notifications
* and handles reconnection attempts if the connection fails
* @return {Promise<void>} A promise that resolves when the subscription is established
* @mermaid
* sequenceDiagram
* participant D as PostgreSQLDispatch
* participant S as subscribeToPostgreSQL
* participant DB as PostgreSQL Database
* participant L as Logger
* D->>S: Call subscribeToPostgreSQL
* S->>S: Check adapter and native
* alt No adapter or native
* S-->>S: throw InternalError
* end
* S->>DB: Connect client from pool
* S->>DB: LISTEN table_changes
* alt Success
* DB-->>S: Subscription established
* S-->>D: Promise resolves
* D->>L: Log successful subscription
* else Error
* DB-->>S: Error
* S->>S: Increment attemptCounter
* alt attemptCounter > 3
* S->>L: Log error
* S-->>D: Promise rejects
* else attemptCounter <= 3
* S->>L: Log retry
* S->>S: Wait timeout
* S->>S: Recursive call to subscribeToPostgreSQL
* end
* end
*/
async initialize() {
const log = this.log.for(this.initialize);
async function subscribeToPostgres() {
if (!this.adapter || !this.native) {
throw new db_decorators_1.InternalError(`No adapter/native observed for dispatch`);
}
try {
this.client = await this.native.connect();
this.client.on("notification", this.notificationHandler.bind(this));
// Listen for table change notifications
// This assumes you have set up triggers in PostgreSQL to NOTIFY on table changes
const res = await this.client.query("LISTEN user_table_changes");
this.attemptCounter = 0;
}
catch (e) {
if (this.client) {
this.client.release();
this.client = undefined;
}
if (++this.attemptCounter > 3) {
return log.error(`Failed to subscribe to Postgres notifications: ${e}`);
}
log.info(`Failed to subscribe to Postgres notifications: ${e}. Retrying in ${this.timeout}ms...`);
await new Promise((resolve) => setTimeout(resolve, this.timeout));
return subscribeToPostgres.call(this);
}
}
subscribeToPostgres
.call(this)
.then(() => {
this.log.info(`Subscribed to Postgres notifications`);
})
.catch((e) => {
throw new db_decorators_1.InternalError(`Failed to subscribe to Postgres notifications: ${e}`);
});
}
/**
* Cleanup method to release resources when the dispatcher is no longer needed
*/
cleanup() {
if (this.client) {
this.client.release();
this.client = undefined;
}
}
}
exports.PostgresDispatch = PostgresDispatch;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUG9zdGdyZXNEaXNwYXRjaC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9Qb3N0Z3Jlc0Rpc3BhdGNoLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHlDQUEwQztBQUUxQywyREFBdUU7QUFFdkU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXVDRztBQUNILE1BQWEsZ0JBQWlCLFNBQVEsZUFBYztJQUtsRCxZQUFvQixVQUFVLElBQUk7UUFDaEMsS0FBSyxFQUFFLENBQUM7UUFEVSxZQUFPLEdBQVAsT0FBTyxDQUFPO1FBSDFCLG1CQUFjLEdBQVcsQ0FBQyxDQUFDO0lBS25DLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7T0FpQkc7SUFDTyxLQUFLLENBQUMsbUJBQW1CLENBQ2pDLFlBQTBCO1FBRTFCLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO1FBRW5ELElBQUksQ0FBQztZQUNILGdGQUFnRjtZQUNoRixNQUFNLE9BQU8sR0FBRyxZQUFZLENBQUMsT0FBaUIsQ0FBQztZQUMvQyxNQUFNLENBQUMsS0FBSyxFQUFFLFNBQVMsRUFBRSxTQUFTLENBQUMsR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ3pELE1BQU0sR0FBRyxHQUFHLFNBQVMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7WUFFakMsSUFBSSxDQUFDLEtBQUssSUFBSSxDQUFDLFNBQVMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsQ0FBQztnQkFDeEMsT0FBTyxHQUFHLENBQUMsS0FBSyxDQUFDLGdDQUFnQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1lBQzlELENBQUM7WUFFRCx3Q0FBd0M7WUFDeEMsSUFBSSxZQUEyQixDQUFDO1lBQ2hDLFFBQVEsU0FBUyxDQUFDLFdBQVcsRUFBRSxFQUFFLENBQUM7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QyxJQUFJLENBQUM7YUFDVixJQUFJLENBQUMsR0FBRyxFQUFFO1lBQ1QsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsc0NBQXNDLENBQUMsQ0FBQztRQUN4RCxDQUFDLENBQUM7YUFDRCxLQUFLLENBQUMsQ0FBQyxDQUFVLEVBQUUsRUFBRTtZQUNwQixNQUFNLElBQUksNkJBQWEsQ0FDckIsa0RBQWtELENBQUMsRUFBRSxDQUN0RCxDQUFDO1FBQ0osQ0FBQyxDQUFDLENBQUM7SUFDUCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxPQUFPO1FBQ1osSUFBSSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDaEIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUN0QixJQUFJLENBQUMsTUFBTSxHQUFHLFNBQVMsQ0FBQztRQUMxQixDQUFDO0lBQ0gsQ0FBQztDQUNGO0FBbktELDRDQW1LQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IERpc3BhdGNoIH0gZnJvbSBcIkBkZWNhZi10cy9jb3JlXCI7XG5pbXBvcnQgeyBQb29sLCBQb29sQ2xpZW50LCBOb3RpZmljYXRpb24gfSBmcm9tIFwicGdcIjtcbmltcG9ydCB7IEludGVybmFsRXJyb3IsIE9wZXJhdGlvbktleXMgfSBmcm9tIFwiQGRlY2FmLXRzL2RiLWRlY29yYXRvcnNcIjtcblxuLyoqXG4gKiBAZGVzY3JpcHRpb24gRGlzcGF0Y2hlciBmb3IgUG9zdGdyZVNRTCBkYXRhYmFzZSBjaGFuZ2UgZXZlbnRzXG4gKiBAc3VtbWFyeSBIYW5kbGVzIHRoZSBzdWJzY3JpcHRpb24gdG8gYW5kIHByb2Nlc3Npbmcgb2YgZGF0YWJhc2UgY2hhbmdlIGV2ZW50cyBmcm9tIGEgUG9zdGdyZVNRTCBkYXRhYmFzZSxcbiAqIG5vdGlmeWluZyBvYnNlcnZlcnMgd2hlbiByZWNvcmRzIGFyZSBjcmVhdGVkLCB1cGRhdGVkLCBvciBkZWxldGVkXG4gKiBAdGVtcGxhdGUgUG9vbCAtIFRoZSBwZyBQb29sIHR5cGVcbiAqIEBwYXJhbSB7bnVtYmVyfSBbdGltZW91dD01MDAwXSAtIFRpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBub3RpZmljYXRpb24gcmVxdWVzdHNcbiAqIEBjbGFzcyBQb3N0Z3Jlc0Rpc3BhdGNoXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogLy8gQ3JlYXRlIGEgZGlzcGF0Y2hlciBmb3IgYSBQb3N0Z3JlU1FMIGRhdGFiYXNlXG4gKiBjb25zdCBwb29sID0gbmV3IFBvb2woe1xuICogICB1c2VyOiAncG9zdGdyZXMnLFxuICogICBwYXNzd29yZDogJ3Bhc3N3b3JkJyxcbiAqICAgaG9zdDogJ2xvY2FsaG9zdCcsXG4gKiAgIHBvcnQ6IDU0MzIsXG4gKiAgIGRhdGFiYXNlOiAnbXlkYidcbiAqIH0pO1xuICogY29uc3QgYWRhcHRlciA9IG5ldyBQb3N0Z3JlU1FMQWRhcHRlckltcGwocG9vbCk7XG4gKiBjb25zdCBkaXNwYXRjaCA9IG5ldyBQb3N0Z3JlU1FMRGlzcGF0Y2goKTtcbiAqXG4gKiAvLyBUaGUgZGlzcGF0Y2hlciB3aWxsIGF1dG9tYXRpY2FsbHkgc3Vic2NyaWJlIHRvIG5vdGlmaWNhdGlvbnNcbiAqIC8vIGFuZCBub3RpZnkgb2JzZXJ2ZXJzIHdoZW4gcmVjb3JkcyBjaGFuZ2VcbiAqIGBgYFxuICogQG1lcm1haWRcbiAqIGNsYXNzRGlhZ3JhbVxuICogICBjbGFzcyBEaXNwYXRjaCB7XG4gKiAgICAgK2luaXRpYWxpemUoKVxuICogICAgICt1cGRhdGVPYnNlcnZlcnMoKVxuICogICB9XG4gKiAgIGNsYXNzIFBvc3RncmVTUUxEaXNwYXRjaCB7XG4gKiAgICAgLW9ic2VydmVyTGFzdFVwZGF0ZT86IHN0cmluZ1xuICogICAgIC1hdHRlbXB0Q291bnRlcjogbnVtYmVyXG4gKiAgICAgLXRpbWVvdXQ6IG51bWJlclxuICogICAgIC1jbGllbnQ/OiBQb29sQ2xpZW50XG4gKiAgICAgK2NvbnN0cnVjdG9yKHRpbWVvdXQpXG4gKiAgICAgI25vdGlmaWNhdGlvbkhhbmRsZXIoKVxuICogICAgICNpbml0aWFsaXplKClcbiAqICAgfVxuICogICBEaXNwYXRjaCA8fC0tIFBvc3RncmVTUUxEaXNwYXRjaFxuICovXG5leHBvcnQgY2xhc3MgUG9zdGdyZXNEaXNwYXRjaCBleHRlbmRzIERpc3BhdGNoPFBvb2w+IHtcbiAgcHJpdmF0ZSBvYnNlcnZlckxhc3RVcGRhdGU/OiBzdHJpbmc7XG4gIHByaXZhdGUgYXR0ZW1wdENvdW50ZXI6IG51bWJlciA9IDA7XG4gIHByaXZhdGUgY2xpZW50PzogUG9vbENsaWVudDtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHRpbWVvdXQgPSA1MDAwKSB7XG4gICAgc3VwZXIoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gUHJvY2Vzc2VzIGRhdGFiYXNlIG5vdGlmaWNhdGlvbiBldmVudHNcbiAgICogQHN1bW1hcnkgSGFuZGxlcyB0aGUgbm90aWZpY2F0aW9ucyBmcm9tIFBvc3RncmVTUUwgTElTVEVOL05PVElGWSBtZWNoYW5pc20sXG4gICAqIGFuZCBub3RpZmllcyBvYnNlcnZlcnMgYWJvdXQgcmVjb3JkIGNoYW5nZXNcbiAgICogQHBhcmFtIHtOb3RpZmljYXRpb259IG5vdGlmaWNhdGlvbiAtIFRoZSBub3RpZmljYXRpb24gZnJvbSBQb3N0Z3JlU1FMXG4gICAqIEByZXR1cm4ge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gYWxsIG5vdGlmaWNhdGlvbnMgaGF2ZSBiZWVuIHByb2Nlc3NlZFxuICAgKiBAbWVybWFpZFxuICAgKiBzZXF1ZW5jZURpYWdyYW1cbiAgICogICBwYXJ0aWNpcGFudCBEIGFzIFBvc3RncmVTUUxEaXNwYXRjaFxuICAgKiAgIHBhcnRpY2lwYW50IEwgYXMgTG9nZ2VyXG4gICAqICAgcGFydGljaXBhbnQgTyBhcyBPYnNlcnZlcnNcbiAgICogICBOb3RlIG92ZXIgRDogUmVjZWl2ZSBub3RpZmljYXRpb24gZnJvbSBQb3N0Z3JlU1FMXG4gICAqICAgRC0+PkQ6IFBhcnNlIG5vdGlmaWNhdGlvbiBwYXlsb2FkXG4gICAqICAgRC0+PkQ6IEV4dHJhY3QgdGFibGUsIG9wZXJhdGlvbiwgYW5kIGlkc1xuICAgKiAgIEQtPj5POiB1cGRhdGVPYnNlcnZlcnModGFibGUsIG9wZXJhdGlvbiwgaWRzKVxuICAgKiAgIEQtPj5EOiBVcGRhdGUgb2JzZXJ2ZXJMYXN0VXBkYXRlXG4gICAqICAgRC0+Pkw6IExvZyBzdWNjZXNzZnVsIGRpc3BhdGNoXG4gICAqL1xuICBwcm90ZWN0ZWQgYXN5bmMgbm90aWZpY2F0aW9uSGFuZGxlcihcbiAgICBub3RpZmljYXRpb246IE5vdGlmaWNhdGlvblxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBsb2cgPSB0aGlzLmxvZy5mb3IodGhpcy5ub3RpZmljYXRpb25IYW5kbGVyKTtcblxuICAgIHRyeSB7XG4gICAgICAvLyBQYXJzZSB0aGUgbm90aWZpY2F0aW9uIHBheWxvYWQgKGV4cGVjdGVkIGZvcm1hdDogdGFibGU6b3BlcmF0aW9uOmlkMSxpZDIsLi4uKVxuICAgICAgY29uc3QgcGF5bG9hZCA9IG5vdGlmaWNhdGlvbi5wYXlsb2FkIGFzIHN0cmluZztcbiAgICAgIGNvbnN0IFt0YWJsZSwgb3BlcmF0aW9uLCBpZHNTdHJpbmddID0gcGF5bG9hZC5zcGxpdChcIjpcIik7XG4gICAgICBjb25zdCBpZHMgPSBpZHNTdHJpbmcuc3BsaXQoXCIsXCIpO1xuXG4gICAgICBpZiAoIXRhYmxlIHx8ICFvcGVyYXRpb24gfHwgIWlkcy5sZW5ndGgpIHtcbiAgICAgICAgcmV0dXJuIGxvZy5lcnJvcihgSW52YWxpZCBub3RpZmljYXRpb24gZm9ybWF0OiAke3BheWxvYWR9YCk7XG4gICAgICB9XG5cbiAgICAgIC8vIE1hcCBvcGVyYXRpb24gc3RyaW5nIHRvIE9wZXJhdGlvbktleXNcbiAgICAgIGxldCBvcGVyYXRpb25LZXk6IE9wZXJhdGlvbktleXM7XG4gICAgICBzd2l0Y2ggKG9wZXJhdGlvbi50b0xvd2VyQ2FzZSgpKSB7XG4gICAgICAgIGNhc2UgXCJpbnNlcnRcIjpcbiAgICAgICAgICBvcGVyYXRpb25LZXkgPSBPcGVyYXRpb25LZXlzLkNSRUFURTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcInVwZGF0ZVwiOlxuICAgICAgICAgIG9wZXJhdGlvbktleSA9IE9wZXJhdGlvbktleXMuVVBEQVRFO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwiZGVsZXRlXCI6XG4gICAgICAgICAgb3BlcmF0aW9uS2V5ID0gT3BlcmF0aW9uS2V5cy5ERUxFVEU7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgcmV0dXJuIGxvZy5lcnJvcihgVW5rbm93biBvcGVyYXRpb246ICR7b3BlcmF0aW9ufWApO1xuICAgICAgfVxuXG4gICAgICAvLyBOb3RpZnkgb2JzZXJ2ZXJzXG4gICAgICBhd2FpdCB0aGlzLnVwZGF0ZU9ic2VydmVycyh0YWJsZSwgb3BlcmF0aW9uS2V5LCBpZHMpO1xuICAgICAgdGhpcy5vYnNlcnZlckxhc3RVcGRhdGUgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCk7XG4gICAgICBsb2cudmVyYm9zZShgT2JzZXJ2ZXIgcmVmcmVzaCBkaXNwYXRjaGVkIGJ5ICR7b3BlcmF0aW9ufSBmb3IgJHt0YWJsZX1gKTtcbiAgICAgIGxvZy5kZWJ1ZyhgcGtzOiAke2lkc31gKTtcbiAgICB9IGNhdGNoIChlOiB1bmtub3duKSB7XG4gICAgICBsb2cuZXJyb3IoYEZhaWxlZCB0byBwcm9jZXNzIG5vdGlmaWNhdGlvbjogJHtlfWApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gSW5pdGlhbGl6ZXMgdGhlIGRpc3BhdGNoZXIgYW5kIHN1YnNjcmliZXMgdG8gZGF0YWJhc2Ugbm90aWZpY2F0aW9uc1xuICAgKiBAc3VtbWFyeSBTZXRzIHVwIHRoZSBMSVNURU4gbWVjaGFuaXNtIHRvIHN1YnNjcmliZSB0byBQb3N0Z3JlU1FMIG5vdGlmaWNhdGlvbnNcbiAgICogYW5kIGhhbmRsZXMgcmVjb25uZWN0aW9uIGF0dGVtcHRzIGlmIHRoZSBjb25uZWN0aW9uIGZhaWxzXG4gICAqIEByZXR1cm4ge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHN1YnNjcmlwdGlvbiBpcyBlc3RhYmxpc2hlZFxuICAgKiBAbWVybWFpZFxuICAgKiBzZXF1ZW5jZURpYWdyYW1cbiAgICogICBwYXJ0aWNpcGFudCBEIGFzIFBvc3RncmVTUUxEaXNwYXRjaFxuICAgKiAgIHBhcnRpY2lwYW50IFMgYXMgc3Vic2NyaWJlVG9Qb3N0Z3JlU1FMXG4gICAqICAgcGFydGljaXBhbnQgREIgYXMgUG9zdGdyZVNRTCBEYXRhYmFzZVxuICAgKiAgIHBhcnRpY2lwYW50IEwgYXMgTG9nZ2VyXG4gICAqICAgRC0+PlM6IENhbGwgc3Vic2NyaWJlVG9Qb3N0Z3JlU1FMXG4gICAqICAgUy0+PlM6IENoZWNrIGFkYXB0ZXIgYW5kIG5hdGl2ZVxuICAgKiAgIGFsdCBObyBhZGFwdGVyIG9yIG5hdGl2ZVxuICAgKiAgICAgUy0tPj5TOiB0aHJvdyBJbnRlcm5hbEVycm9yXG4gICAqICAgZW5kXG4gICAqICAgUy0+PkRCOiBDb25uZWN0IGNsaWVudCBmcm9tIHBvb2xcbiAgICogICBTLT4+REI6IExJU1RFTiB0YWJsZV9jaGFuZ2VzXG4gICAqICAgYWx0IFN1Y2Nlc3NcbiAgICogICAgIERCLS0+PlM6IFN1YnNjcmlwdGlvbiBlc3RhYmxpc2hlZFxuICAgKiAgICAgUy0tPj5EOiBQcm9taXNlIHJlc29sdmVzXG4gICAqICAgICBELT4+TDogTG9nIHN1Y2Nlc3NmdWwgc3Vic2NyaXB0aW9uXG4gICAqICAgZWxzZSBFcnJvclxuICAgKiAgICAgREItLT4+UzogRXJyb3JcbiAgICogICAgIFMtPj5TOiBJbmNyZW1lbnQgYXR0ZW1wdENvdW50ZXJcbiAgICogICAgIGFsdCBhdHRlbXB0Q291bnRlciA+IDNcbiAgICogICAgICAgUy0+Pkw6IExvZyBlcnJvclxuICAgKiAgICAgICBTLS0+PkQ6IFByb21pc2UgcmVqZWN0c1xuICAgKiAgICAgZWxzZSBhdHRlbXB0Q291bnRlciA8PSAzXG4gICAqICAgICAgIFMtPj5MOiBMb2cgcmV0cnlcbiAgICogICAgICAgUy0+PlM6IFdhaXQgdGltZW91dFxuICAgKiAgICAgICBTLT4+UzogUmVjdXJzaXZlIGNhbGwgdG8gc3Vic2NyaWJlVG9Qb3N0Z3JlU1FMXG4gICAqICAgICBlbmRcbiAgICogICBlbmRcbiAgICovXG4gIHByb3RlY3RlZCBvdmVycmlkZSBhc3luYyBpbml0aWFsaXplKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGxvZyA9IHRoaXMubG9nLmZvcih0aGlzLmluaXRpYWxpemUpO1xuXG4gICAgYXN5bmMgZnVuY3Rpb24gc3Vic2NyaWJlVG9Qb3N0Z3Jlcyh0aGlzOiBQb3N0Z3Jlc0Rpc3BhdGNoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICBpZiAoIXRoaXMuYWRhcHRlciB8fCAhdGhpcy5uYXRpdmUpIHtcbiAgICAgICAgdGhyb3cgbmV3IEludGVybmFsRXJyb3IoYE5vIGFkYXB0ZXIvbmF0aXZlIG9ic2VydmVkIGZvciBkaXNwYXRjaGApO1xuICAgICAgfVxuXG4gICAgICB0cnkge1xuICAgICAgICB0aGlzLmNsaWVudCA9IGF3YWl0IHRoaXMubmF0aXZlLmNvbm5lY3QoKTtcblxuICAgICAgICB0aGlzLmNsaWVudC5vbihcIm5vdGlmaWNhdGlvblwiLCB0aGlzLm5vdGlmaWNhdGlvbkhhbmRsZXIuYmluZCh0aGlzKSk7XG5cbiAgICAgICAgLy8gTGlzdGVuIGZvciB0YWJsZSBjaGFuZ2Ugbm90aWZpY2F0aW9uc1xuICAgICAgICAvLyBUaGlzIGFzc3VtZXMgeW91IGhhdmUgc2V0IHVwIHRyaWdnZXJzIGluIFBvc3RncmVTUUwgdG8gTk9USUZZIG9uIHRhYmxlIGNoYW5nZXNcbiAgICAgICAgY29uc3QgcmVzID0gYXdhaXQgdGhpcy5jbGllbnQucXVlcnkoXCJMSVNURU4gdXNlcl90YWJsZV9jaGFuZ2VzXCIpO1xuXG4gICAgICAgIHRoaXMuYXR0ZW1wdENvdW50ZXIgPSAwO1xuICAgICAgfSBjYXRjaCAoZTogdW5rbm93bikge1xuICAgICAgICBpZiAodGhpcy5jbGllbnQpIHtcbiAgICAgICAgICB0aGlzLmNsaWVudC5yZWxlYXNlKCk7XG4gICAgICAgICAgdGhpcy5jbGllbnQgPSB1bmRlZmluZWQ7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoKyt0aGlzLmF0dGVtcHRDb3VudGVyID4gMykge1xuICAgICAgICAgIHJldHVybiBsb2cuZXJyb3IoXG4gICAgICAgICAgICBgRmFpbGVkIHRvIHN1YnNjcmliZSB0byBQb3N0Z3JlcyBub3RpZmljYXRpb25zOiAke2V9YFxuICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICBsb2cuaW5mbyhcbiAgICAgICAgICBgRmFpbGVkIHRvIHN1YnNjcmliZSB0byBQb3N0Z3JlcyBub3RpZmljYXRpb25zOiAke2V9LiBSZXRyeWluZyBpbiAke3RoaXMudGltZW91dH1tcy4uLmBcbiAgICAgICAgKTtcblxuICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gc2V0VGltZW91dChyZXNvbHZlLCB0aGlzLnRpbWVvdXQpKTtcbiAgICAgICAgcmV0dXJuIHN1YnNjcmliZVRvUG9zdGdyZXMuY2FsbCh0aGlzKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBzdWJzY3JpYmVUb1Bvc3RncmVzXG4gICAgICAuY2FsbCh0aGlzKVxuICAgICAgLnRoZW4oKCkgPT4ge1xuICAgICAgICB0aGlzLmxvZy5pbmZvKGBTdWJzY3JpYmVkIHRvIFBvc3RncmVzIG5vdGlmaWNhdGlvbnNgKTtcbiAgICAgIH0pXG4gICAgICAuY2F0Y2goKGU6IHVua25vd24pID0+IHtcbiAgICAgICAgdGhyb3cgbmV3IEludGVybmFsRXJyb3IoXG4gICAgICAgICAgYEZhaWxlZCB0byBzdWJzY3JpYmUgdG8gUG9zdGdyZXMgbm90aWZpY2F0aW9uczogJHtlfWBcbiAgICAgICAgKTtcbiAgICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIENsZWFudXAgbWV0aG9kIHRvIHJlbGVhc2UgcmVzb3VyY2VzIHdoZW4gdGhlIGRpc3BhdGNoZXIgaXMgbm8gbG9uZ2VyIG5lZWRlZFxuICAgKi9cbiAgcHVibGljIGNsZWFudXAoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuY2xpZW50KSB7XG4gICAgICB0aGlzLmNsaWVudC5yZWxlYXNlKCk7XG4gICAgICB0aGlzLmNsaWVudCA9IHVuZGVmaW5lZDtcbiAgICB9XG4gIH1cbn1cbiJdfQ==