@decaf-ts/core
Version:
Core persistence module for the decaf framework
191 lines • 24.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Dispatch = void 0;
const db_decorators_1 = require("@decaf-ts/db-decorators");
const Adapter_1 = require("./Adapter.cjs");
const errors_1 = require("./errors.cjs");
const logging_1 = require("@decaf-ts/logging");
/**
* @description Dispatches database operation events to observers
* @summary The Dispatch class implements the Observable interface and is responsible for intercepting
* database operations from an Adapter and notifying observers when changes occur. It uses proxies to
* wrap the adapter's CRUD methods and automatically trigger observer updates after operations complete.
* @template Y - The native database driver type
* @param {void} - No constructor parameters
* @class Dispatch
* @example
* ```typescript
* // Creating and using a Dispatch instance
* const dispatch = new Dispatch<PostgresDriver>();
*
* // Connect it to an adapter
* const adapter = new PostgresAdapter(connection);
* dispatch.observe(adapter);
*
* // Now any CRUD operations on the adapter will automatically
* // trigger observer notifications
* await adapter.create('users', 123, userModel);
* // Observers will be notified about the creation
*
* // When done, you can disconnect
* dispatch.unObserve(adapter);
* ```
*/
class Dispatch {
/**
* @description Accessor for the logger
* @summary Gets or initializes the logger for this dispatch instance
* @return {Logger} The logger instance
*/
get log() {
if (!this.logger)
this.logger = logging_1.Logging.for(this).for(this.adapter);
return this.logger;
}
/**
* @description Creates a new Dispatch instance
* @summary Initializes a new Dispatch instance without any adapter
*/
constructor() { }
/**
* @description Initializes the dispatch by proxying adapter methods
* @summary Sets up proxies on the adapter's CRUD methods to intercept operations and notify observers.
* This method is called automatically when an adapter is observed.
* @return {Promise<void>} A promise that resolves when initialization is complete
* @mermaid
* sequenceDiagram
* participant Dispatch
* participant Adapter
* participant Proxy
*
* Dispatch->>Dispatch: initialize()
* Dispatch->>Dispatch: Check if adapter exists
* alt No adapter
* Dispatch-->>Dispatch: Throw InternalError
* end
*
* loop For each CRUD method
* Dispatch->>Adapter: Check if method exists
* alt Method doesn't exist
* Dispatch-->>Dispatch: Throw InternalError
* end
*
* Dispatch->>Adapter: Get property descriptor
* loop While descriptor not found
* Dispatch->>Adapter: Check prototype chain
* end
*
* alt Descriptor not found or not writable
* Dispatch->>Dispatch: Log error and continue
* else Descriptor found and writable
* Dispatch->>Proxy: Create proxy for method
* Dispatch->>Adapter: Replace method with proxy
* end
* end
*/
async initialize() {
if (!this.adapter)
throw new db_decorators_1.InternalError(`No adapter observed for dispatch`);
const adapter = this.adapter;
[
db_decorators_1.OperationKeys.CREATE,
db_decorators_1.OperationKeys.UPDATE,
db_decorators_1.OperationKeys.DELETE,
db_decorators_1.BulkCrudOperationKeys.CREATE_ALL,
db_decorators_1.BulkCrudOperationKeys.UPDATE_ALL,
db_decorators_1.BulkCrudOperationKeys.DELETE_ALL,
].forEach((method) => {
if (!adapter[method])
throw new db_decorators_1.InternalError(`Method ${method} not found in ${adapter.alias} adapter to bind Observables Dispatch`);
let descriptor = Object.getOwnPropertyDescriptor(adapter, method);
let proto = adapter;
while (!descriptor && proto !== Object.prototype) {
proto = Object.getPrototypeOf(proto);
descriptor = Object.getOwnPropertyDescriptor(proto, method);
}
if (!descriptor || !descriptor.writable) {
this.log.error(`Could not find method ${method} to bind Observables Dispatch`);
return;
}
function bulkToSingle(method) {
switch (method) {
case db_decorators_1.BulkCrudOperationKeys.CREATE_ALL:
return db_decorators_1.OperationKeys.CREATE;
case db_decorators_1.BulkCrudOperationKeys.UPDATE_ALL:
return db_decorators_1.OperationKeys.UPDATE;
case db_decorators_1.BulkCrudOperationKeys.DELETE_ALL:
return db_decorators_1.OperationKeys.DELETE;
default:
return method;
}
}
// @ts-expect-error because there are read only properties
adapter[method] = new Proxy(adapter[method], {
apply: async (target, thisArg, argArray) => {
const [tableName, ids] = argArray;
const result = await target.apply(thisArg, argArray);
this.updateObservers(tableName, bulkToSingle(method), ids)
.then(() => {
this.log.verbose(`Observer refresh dispatched by ${method} for ${tableName}`);
this.log.debug(`pks: ${ids}`);
})
.catch((e) => this.log.error(`Failed to dispatch observer refresh for ${method} on ${tableName}: ${e}`));
return result;
},
});
});
}
/**
* @description Closes the dispatch
* @summary Performs any necessary cleanup when the dispatch is no longer needed
* @return {Promise<void>} A promise that resolves when closing is complete
*/
async close() {
// to nothing in this instance but may be required for closing connections
}
/**
* @description Starts observing an adapter
* @summary Connects this dispatch to an adapter to monitor its operations
* @param {Adapter<Y, any, any, any>} observer - The adapter to observe
* @return {void}
*/
observe(observer) {
if (!(observer instanceof Adapter_1.Adapter))
throw new errors_1.UnsupportedError("Only Adapters can be observed by dispatch");
this.adapter = observer;
this.native = observer.native;
this.models = Adapter_1.Adapter.models(this.adapter.alias);
this.initialize().then(() => this.log.verbose(`Dispatch initialized for ${this.adapter.alias} adapter`));
}
/**
* @description Stops observing an adapter
* @summary Disconnects this dispatch from an adapter
* @param {Observer} observer - The adapter to stop observing
* @return {void}
*/
unObserve(observer) {
if (this.adapter !== observer)
throw new errors_1.UnsupportedError("Only the adapter that was used to observe can be unobserved");
this.adapter = undefined;
}
/**
* @description Updates observers about a database event
* @summary Notifies observers about a change in the database
* @param {string} table - The name of the table where the change occurred
* @param {OperationKeys|BulkCrudOperationKeys|string} event - The type of operation that occurred
* @param {EventIds} id - The identifier(s) of the affected record(s)
* @return {Promise<void>} A promise that resolves when all observers have been notified
*/
async updateObservers(table, event, id) {
if (!this.adapter)
throw new db_decorators_1.InternalError(`No adapter observed for dispatch`);
try {
await this.adapter.refresh(table, event, id);
}
catch (e) {
throw new db_decorators_1.InternalError(`Failed to refresh dispatch: ${e}`);
}
}
}
exports.Dispatch = Dispatch;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRGlzcGF0Y2guanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcGVyc2lzdGVuY2UvRGlzcGF0Y2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsMkRBSWlDO0FBR2pDLDJDQUFvQztBQUNwQyx5Q0FBNEM7QUFDNUMsK0NBQW9EO0FBR3BEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBeUJHO0FBQ0gsTUFBYSxRQUFRO0lBeUJuQjs7OztPQUlHO0lBQ0gsSUFBYyxHQUFHO1FBQ2YsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNO1lBQ2QsSUFBSSxDQUFDLE1BQU0sR0FBRyxpQkFBTyxDQUFDLEdBQUcsQ0FBQyxJQUFXLENBQUMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLE9BQWMsQ0FBQyxDQUFDO1FBQ2xFLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQztJQUNyQixDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsZ0JBQWUsQ0FBQztJQUVoQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7T0FtQ0c7SUFDTyxLQUFLLENBQUMsVUFBVTtRQUN4QixJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU87WUFDZixNQUFNLElBQUksNkJBQWEsQ0FBQyxrQ0FBa0MsQ0FBQyxDQUFDO1FBQzlELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxPQUFvQyxDQUFDO1FBRXhEO1lBQ0UsNkJBQWEsQ0FBQyxNQUFNO1lBQ3BCLDZCQUFhLENBQUMsTUFBTTtZQUNwQiw2QkFBYSxDQUFDLE1BQU07WUFDcEIscUNBQXFCLENBQUMsVUFBVTtZQUNoQyxxQ0FBcUIsQ0FBQyxVQUFVO1lBQ2hDLHFDQUFxQixDQUFDLFVBQVU7U0FFbkMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxNQUFNLEVBQUUsRUFBRTtZQUNuQixJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQztnQkFDbEIsTUFBTSxJQUFJLDZCQUFhLENBQ3JCLFVBQVUsTUFBTSxpQkFBaUIsT0FB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LENBQUMsQ0FBQztRQUNqRCxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUMxQixJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FDZCw0QkFBNEIsSUFBSSxDQUFDLE9BQVEsQ0FBQyxLQUFLLFVBQVUsQ0FDMUQsQ0FDRixDQUFDO0lBQ0osQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0gsU0FBUyxDQUFDLFFBQWtCO1FBQzFCLElBQUksSUFBSSxDQUFDLE9BQU8sS0FBSyxRQUFRO1lBQzNCLE1BQU0sSUFBSSx5QkFBZ0IsQ0FDeEIsNkRBQTZELENBQzlELENBQUM7UUFDSixJQUFJLENBQUMsT0FBTyxHQUFHLFNBQVMsQ0FBQztJQUMzQixDQUFDO0lBRUQ7Ozs7Ozs7T0FPRztJQUNILEtBQUssQ0FBQyxlQUFlLENBQ25CLEtBQWEsRUFDYixLQUFxRCxFQUNyRCxFQUFZO1FBRVosSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPO1lBQ2YsTUFBTSxJQUFJLDZCQUFhLENBQUMsa0NBQWtDLENBQUMsQ0FBQztRQUM5RCxJQUFJLENBQUM7WUFDSCxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDL0MsQ0FBQztRQUFDLE9BQU8sQ0FBVSxFQUFFLENBQUM7WUFDcEIsTUFBTSxJQUFJLDZCQUFhLENBQUMsK0JBQStCLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDOUQsQ0FBQztJQUNILENBQUM7Q0FDRjtBQWhORCw0QkFnTkMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBJbnRlcm5hbEVycm9yLFxuICBPcGVyYXRpb25LZXlzLFxuICBCdWxrQ3J1ZE9wZXJhdGlvbktleXMsXG59IGZyb20gXCJAZGVjYWYtdHMvZGItZGVjb3JhdG9yc1wiO1xuaW1wb3J0IHsgTW9kZWxDb25zdHJ1Y3RvciB9IGZyb20gXCJAZGVjYWYtdHMvZGVjb3JhdG9yLXZhbGlkYXRpb25cIjtcbmltcG9ydCB7IE9ic2VydmFibGUsIE9ic2VydmVyIH0gZnJvbSBcIi4uL2ludGVyZmFjZXNcIjtcbmltcG9ydCB7IEFkYXB0ZXIgfSBmcm9tIFwiLi9BZGFwdGVyXCI7XG5pbXBvcnQgeyBVbnN1cHBvcnRlZEVycm9yIH0gZnJvbSBcIi4vZXJyb3JzXCI7XG5pbXBvcnQgeyBMb2dnZXIsIExvZ2dpbmcgfSBmcm9tIFwiQGRlY2FmLXRzL2xvZ2dpbmdcIjtcbmltcG9ydCB7IEV2ZW50SWRzIH0gZnJvbSBcIi4vdHlwZXNcIjtcblxuLyoqXG4gKiBAZGVzY3JpcHRpb24gRGlzcGF0Y2hlcyBkYXRhYmFzZSBvcGVyYXRpb24gZXZlbnRzIHRvIG9ic2VydmVyc1xuICogQHN1bW1hcnkgVGhlIERpc3BhdGNoIGNsYXNzIGltcGxlbWVudHMgdGhlIE9ic2VydmFibGUgaW50ZXJmYWNlIGFuZCBpcyByZXNwb25zaWJsZSBmb3IgaW50ZXJjZXB0aW5nXG4gKiBkYXRhYmFzZSBvcGVyYXRpb25zIGZyb20gYW4gQWRhcHRlciBhbmQgbm90aWZ5aW5nIG9ic2VydmVycyB3aGVuIGNoYW5nZXMgb2NjdXIuIEl0IHVzZXMgcHJveGllcyB0b1xuICogd3JhcCB0aGUgYWRhcHRlcidzIENSVUQgbWV0aG9kcyBhbmQgYXV0b21hdGljYWxseSB0cmlnZ2VyIG9ic2VydmVyIHVwZGF0ZXMgYWZ0ZXIgb3BlcmF0aW9ucyBjb21wbGV0ZS5cbiAqIEB0ZW1wbGF0ZSBZIC0gVGhlIG5hdGl2ZSBkYXRhYmFzZSBkcml2ZXIgdHlwZVxuICogQHBhcmFtIHt2b2lkfSAtIE5vIGNvbnN0cnVjdG9yIHBhcmFtZXRlcnNcbiAqIEBjbGFzcyBEaXNwYXRjaFxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIC8vIENyZWF0aW5nIGFuZCB1c2luZyBhIERpc3BhdGNoIGluc3RhbmNlXG4gKiBjb25zdCBkaXNwYXRjaCA9IG5ldyBEaXNwYXRjaDxQb3N0Z3Jlc0RyaXZlcj4oKTtcbiAqXG4gKiAvLyBDb25uZWN0IGl0IHRvIGFuIGFkYXB0ZXJcbiAqIGNvbnN0IGFkYXB0ZXIgPSBuZXcgUG9zdGdyZXNBZGFwdGVyKGNvbm5lY3Rpb24pO1xuICogZGlzcGF0Y2gub2JzZXJ2ZShhZGFwdGVyKTtcbiAqXG4gKiAvLyBOb3cgYW55IENSVUQgb3BlcmF0aW9ucyBvbiB0aGUgYWRhcHRlciB3aWxsIGF1dG9tYXRpY2FsbHlcbiAqIC8vIHRyaWdnZXIgb2JzZXJ2ZXIgbm90aWZpY2F0aW9uc1xuICogYXdhaXQgYWRhcHRlci5jcmVhdGUoJ3VzZXJzJywgMTIzLCB1c2VyTW9kZWwpO1xuICogLy8gT2JzZXJ2ZXJzIHdpbGwgYmUgbm90aWZpZWQgYWJvdXQgdGhlIGNyZWF0aW9uXG4gKlxuICogLy8gV2hlbiBkb25lLCB5b3UgY2FuIGRpc2Nvbm5lY3RcbiAqIGRpc3BhdGNoLnVuT2JzZXJ2ZShhZGFwdGVyKTtcbiAqIGBgYFxuICovXG5leHBvcnQgY2xhc3MgRGlzcGF0Y2g8WT4gaW1wbGVtZW50cyBPYnNlcnZhYmxlIHtcbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBUaGUgYWRhcHRlciBiZWluZyBvYnNlcnZlZFxuICAgKiBAc3VtbWFyeSBSZWZlcmVuY2UgdG8gdGhlIGRhdGFiYXNlIGFkYXB0ZXIgd2hvc2Ugb3BlcmF0aW9ucyBhcmUgYmVpbmcgbW9uaXRvcmVkXG4gICAqL1xuICBwcm90ZWN0ZWQgYWRhcHRlcj86IEFkYXB0ZXI8WSwgYW55LCBhbnksIGFueT47XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBUaGUgbmF0aXZlIGRhdGFiYXNlIGRyaXZlclxuICAgKiBAc3VtbWFyeSBSZWZlcmVuY2UgdG8gdGhlIHVuZGVybHlpbmcgZGF0YWJhc2UgZHJpdmVyIGZyb20gdGhlIGFkYXB0ZXJcbiAgICovXG4gIHByb3RlY3RlZCBuYXRpdmU/OiBZO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gTGlzdCBvZiBtb2RlbCBjb25zdHJ1Y3RvcnNcbiAgICogQHN1bW1hcnkgQXJyYXkgb2YgbW9kZWwgY29uc3RydWN0b3JzIHRoYXQgYXJlIHJlZ2lzdGVyZWQgd2l0aCB0aGUgYWRhcHRlclxuICAgKi9cbiAgcHJvdGVjdGVkIG1vZGVscyE6IE1vZGVsQ29uc3RydWN0b3I8YW55PltdO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gTG9nZ2VyIGluc3RhbmNlXG4gICAqIEBzdW1tYXJ5IExvZ2dlciBmb3IgcmVjb3JkaW5nIGRpc3BhdGNoIGFjdGl2aXRpZXNcbiAgICovXG4gIHByaXZhdGUgbG9nZ2VyITogTG9nZ2VyO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQWNjZXNzb3IgZm9yIHRoZSBsb2dnZXJcbiAgICogQHN1bW1hcnkgR2V0cyBvciBpbml0aWFsaXplcyB0aGUgbG9nZ2VyIGZvciB0aGlzIGRpc3BhdGNoIGluc3RhbmNlXG4gICAqIEByZXR1cm4ge0xvZ2dlcn0gVGhlIGxvZ2dlciBpbnN0YW5jZVxuICAgKi9cbiAgcHJvdGVjdGVkIGdldCBsb2coKSB7XG4gICAgaWYgKCF0aGlzLmxvZ2dlcilcbiAgICAgIHRoaXMubG9nZ2VyID0gTG9nZ2luZy5mb3IodGhpcyBhcyBhbnkpLmZvcih0aGlzLmFkYXB0ZXIgYXMgYW55KTtcbiAgICByZXR1cm4gdGhpcy5sb2dnZXI7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIENyZWF0ZXMgYSBuZXcgRGlzcGF0Y2ggaW5zdGFuY2VcbiAgICogQHN1bW1hcnkgSW5pdGlhbGl6ZXMgYSBuZXcgRGlzcGF0Y2ggaW5zdGFuY2Ugd2l0aG91dCBhbnkgYWRhcHRlclxuICAgKi9cbiAgY29uc3RydWN0b3IoKSB7fVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gSW5pdGlhbGl6ZXMgdGhlIGRpc3BhdGNoIGJ5IHByb3h5aW5nIGFkYXB0ZXIgbWV0aG9kc1xuICAgKiBAc3VtbWFyeSBTZXRzIHVwIHByb3hpZXMgb24gdGhlIGFkYXB0ZXIncyBDUlVEIG1ldGhvZHMgdG8gaW50ZXJjZXB0IG9wZXJhdGlvbnMgYW5kIG5vdGlmeSBvYnNlcnZlcnMuXG4gICAqIFRoaXMgbWV0aG9kIGlzIGNhbGxlZCBhdXRvbWF0aWNhbGx5IHdoZW4gYW4gYWRhcHRlciBpcyBvYnNlcnZlZC5cbiAgICogQHJldHVybiB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiBpbml0aWFsaXphdGlvbiBpcyBjb21wbGV0ZVxuICAgKiBAbWVybWFpZFxuICAgKiBzZXF1ZW5jZURpYWdyYW1cbiAgICogICBwYXJ0aWNpcGFudCBEaXNwYXRjaFxuICAgKiAgIHBhcnRpY2lwYW50IEFkYXB0ZXJcbiAgICogICBwYXJ0aWNpcGFudCBQcm94eVxuICAgKlxuICAgKiAgIERpc3BhdGNoLT4+RGlzcGF0Y2g6IGluaXRpYWxpemUoKVxuICAgKiAgIERpc3BhdGNoLT4+RGlzcGF0Y2g6IENoZWNrIGlmIGFkYXB0ZXIgZXhpc3RzXG4gICAqICAgYWx0IE5vIGFkYXB0ZXJcbiAgICogICAgIERpc3BhdGNoLS0+PkRpc3BhdGNoOiBUaHJvdyBJbnRlcm5hbEVycm9yXG4gICAqICAgZW5kXG4gICAqXG4gICAqICAgbG9vcCBGb3IgZWFjaCBDUlVEIG1ldGhvZFxuICAgKiAgICAgRGlzcGF0Y2gtPj5BZGFwdGVyOiBDaGVjayBpZiBtZXRob2QgZXhpc3RzXG4gICAqICAgICBhbHQgTWV0aG9kIGRvZXNuJ3QgZXhpc3RcbiAgICogICAgICAgRGlzcGF0Y2gtLT4+RGlzcGF0Y2g6IFRocm93IEludGVybmFsRXJyb3JcbiAgICogICAgIGVuZFxuICAgKlxuICAgKiAgICAgRGlzcGF0Y2gtPj5BZGFwdGVyOiBHZXQgcHJvcGVydHkgZGVzY3JpcHRvclxuICAgKiAgICAgbG9vcCBXaGlsZSBkZXNjcmlwdG9yIG5vdCBmb3VuZFxuICAgKiAgICAgICBEaXNwYXRjaC0+PkFkYXB0ZXI6IENoZWNrIHByb3RvdHlwZSBjaGFpblxuICAgKiAgICAgZW5kXG4gICAqXG4gICAqICAgICBhbHQgRGVzY3JpcHRvciBub3QgZm91bmQgb3Igbm90IHdyaXRhYmxlXG4gICAqICAgICAgIERpc3BhdGNoLT4+RGlzcGF0Y2g6IExvZyBlcnJvciBhbmQgY29udGludWVcbiAgICogICAgIGVsc2UgRGVzY3JpcHRvciBmb3VuZCBhbmQgd3JpdGFibGVcbiAgICogICAgICAgRGlzcGF0Y2gtPj5Qcm94eTogQ3JlYXRlIHByb3h5IGZvciBtZXRob2RcbiAgICogICAgICAgRGlzcGF0Y2gtPj5BZGFwdGVyOiBSZXBsYWNlIG1ldGhvZCB3aXRoIHByb3h5XG4gICAqICAgICBlbmRcbiAgICogICBlbmRcbiAgICovXG4gIHByb3RlY3RlZCBhc3luYyBpbml0aWFsaXplKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICghdGhpcy5hZGFwdGVyKVxuICAgICAgdGhyb3cgbmV3IEludGVybmFsRXJyb3IoYE5vIGFkYXB0ZXIgb2JzZXJ2ZWQgZm9yIGRpc3BhdGNoYCk7XG4gICAgY29uc3QgYWRhcHRlciA9IHRoaXMuYWRhcHRlciBhcyBBZGFwdGVyPFksIGFueSwgYW55LCBhbnk+O1xuICAgIChcbiAgICAgIFtcbiAgICAgICAgT3BlcmF0aW9uS2V5cy5DUkVBVEUsXG4gICAgICAgIE9wZXJhdGlvbktleXMuVVBEQVRFLFxuICAgICAgICBPcGVyYXRpb25LZXlzLkRFTEVURSxcbiAgICAgICAgQnVsa0NydWRPcGVyYXRpb25LZXlzLkNSRUFURV9BTEwsXG4gICAgICAgIEJ1bGtDcnVkT3BlcmF0aW9uS2V5cy5VUERBVEVfQUxMLFxuICAgICAgICBCdWxrQ3J1ZE9wZXJhdGlvbktleXMuREVMRVRFX0FMTCxcbiAgICAgIF0gYXMgKGtleW9mIEFkYXB0ZXI8WSwgYW55LCBhbnksIGFueT4pW11cbiAgICApLmZvckVhY2goKG1ldGhvZCkgPT4ge1xuICAgICAgaWYgKCFhZGFwdGVyW21ldGhvZF0pXG4gICAgICAgIHRocm93IG5ldyBJbnRlcm5hbEVycm9yKFxuICAgICAgICAgIGBNZXRob2QgJHttZXRob2R9IG5vdCBmb3VuZCBpbiAke2FkYXB0ZXIuYWxpYXN9IGFkYXB0ZXIgdG8gYmluZCBPYnNlcnZhYmxlcyBEaXNwYXRjaGBcbiAgICAgICAgKTtcblxuICAgICAgbGV0IGRlc2NyaXB0b3IgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKGFkYXB0ZXIsIG1ldGhvZCk7XG4gICAgICBsZXQgcHJvdG86IGFueSA9IGFkYXB0ZXI7XG4gICAgICB3aGlsZSAoIWRlc2NyaXB0b3IgJiYgcHJvdG8gIT09IE9iamVjdC5wcm90b3R5cGUpIHtcbiAgICAgICAgcHJvdG8gPSBPYmplY3QuZ2V0UHJvdG90eXBlT2YocHJvdG8pO1xuICAgICAgICBkZXNjcmlwdG9yID0gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihwcm90bywgbWV0aG9kKTtcbiAgICAgIH1cblxuICAgICAgaWYgKCFkZXNjcmlwdG9yIHx8ICFkZXNjcmlwdG9yLndyaXRhYmxlKSB7XG4gICAgICAgIHRoaXMubG9nLmVycm9yKFxuICAgICAgICAgIGBDb3VsZCBub3QgZmluZCBtZXRob2QgJHttZXRob2R9IHRvIGJpbmQgT2JzZXJ2YWJsZXMgRGlzcGF0Y2hgXG4gICAgICAgICk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGZ1bmN0aW9uIGJ1bGtUb1NpbmdsZShtZXRob2Q6IHN0cmluZykge1xuICAgICAgICBzd2l0Y2ggKG1ldGhvZCkge1xuICAgICAgICAgIGNhc2UgQnVsa0NydWRPcGVyYXRpb25LZXlzLkNSRUFURV9BTEw6XG4gICAgICAgICAgICByZXR1cm4gT3BlcmF0aW9uS2V5cy5DUkVBVEU7XG4gICAgICAgICAgY2FzZSBCdWxrQ3J1ZE9wZXJhdGlvbktleXMuVVBEQVRFX0FMTDpcbiAgICAgICAgICAgIHJldHVybiBPcGVyYXRpb25LZXlzLlVQREFURTtcbiAgICAgICAgICBjYXNlIEJ1bGtDcnVkT3BlcmF0aW9uS2V5cy5ERUxFVEVfQUxMOlxuICAgICAgICAgICAgcmV0dXJuIE9wZXJhdGlvbktleXMuREVMRVRFO1xuICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICByZXR1cm4gbWV0aG9kO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICAvLyBAdHMtZXhwZWN0LWVycm9yIGJlY2F1c2UgdGhlcmUgYXJlIHJlYWQgb25seSBwcm9wZXJ0aWVzXG4gICAgICBhZGFwdGVyW21ldGhvZF0gPSBuZXcgUHJveHkoYWRhcHRlclttZXRob2RdLCB7XG4gICAgICAgIGFwcGx5OiBhc3luYyAodGFyZ2V0OiBhbnksIHRoaXNBcmcsIGFyZ0FycmF5OiBhbnlbXSkgPT4ge1xuICAgICAgICAgIGNvbnN0IFt0YWJsZU5hbWUsIGlkc10gPSBhcmdBcnJheTtcbiAgICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0YXJnZXQuYXBwbHkodGhpc0FyZywgYXJnQXJyYXkpO1xuICAgICAgICAgIHRoaXMudXBkYXRlT2JzZXJ2ZXJzKHRhYmxlTmFtZSwgYnVsa1RvU2luZ2xlKG1ldGhvZCksIGlkcyBhcyBFdmVudElkcylcbiAgICAgICAgICAgIC50aGVuKCgpID0+IHtcbiAgICAgICAgICAgICAgdGhpcy5sb2cudmVyYm9zZShcbiAgICAgICAgICAgICAgICBgT2JzZXJ2ZXIgcmVmcmVzaCBkaXNwYXRjaGVkIGJ5ICR7bWV0aG9kfSBmb3IgJHt0YWJsZU5hbWV9YFxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICB0aGlzLmxvZy5kZWJ1ZyhgcGtzOiAke2lkc31gKTtcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAuY2F0Y2goKGU6IHVua25vd24pID0+XG4gICAgICAgICAgICAgIHRoaXMubG9nLmVycm9yKFxuICAgICAgICAgICAgICAgIGBGYWlsZWQgdG8gZGlzcGF0Y2ggb2JzZXJ2ZXIgcmVmcmVzaCBmb3IgJHttZXRob2R9IG9uICR7dGFibGVOYW1lfTogJHtlfWBcbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgICB9LFxuICAgICAgfSk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIENsb3NlcyB0aGUgZGlzcGF0Y2hcbiAgICogQHN1bW1hcnkgUGVyZm9ybXMgYW55IG5lY2Vzc2FyeSBjbGVhbnVwIHdoZW4gdGhlIGRpc3BhdGNoIGlzIG5vIGxvbmdlciBuZWVkZWRcbiAgICogQHJldHVybiB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiBjbG9zaW5nIGlzIGNvbXBsZXRlXG4gICAqL1xuICBhc3luYyBjbG9zZSgpIHtcbiAgICAvLyB0byBub3RoaW5nIGluIHRoaXMgaW5zdGFuY2UgYnV0IG1heSBiZSByZXF1aXJlZCBmb3IgY2xvc2luZyBjb25uZWN0aW9uc1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBTdGFydHMgb2JzZXJ2aW5nIGFuIGFkYXB0ZXJcbiAgICogQHN1bW1hcnkgQ29ubmVjdHMgdGhpcyBkaXNwYXRjaCB0byBhbiBhZGFwdGVyIHRvIG1vbml0b3IgaXRzIG9wZXJhdGlvbnNcbiAgICogQHBhcmFtIHtBZGFwdGVyPFksIGFueSwgYW55LCBhbnk+fSBvYnNlcnZlciAtIFRoZSBhZGFwdGVyIHRvIG9ic2VydmVcbiAgICogQHJldHVybiB7dm9pZH1cbiAgICovXG4gIG9ic2VydmUob2JzZXJ2ZXI6IEFkYXB0ZXI8WSwgYW55LCBhbnksIGFueT4pOiB2b2lkIHtcbiAgICBpZiAoIShvYnNlcnZlciBpbnN0YW5jZW9mIEFkYXB0ZXIpKVxuICAgICAgdGhyb3cgbmV3IFVuc3VwcG9ydGVkRXJyb3IoXCJPbmx5IEFkYXB0ZXJzIGNhbiBiZSBvYnNlcnZlZCBieSBkaXNwYXRjaFwiKTtcbiAgICB0aGlzLmFkYXB0ZXIgPSBvYnNlcnZlcjtcbiAgICB0aGlzLm5hdGl2ZSA9IG9ic2VydmVyLm5hdGl2ZTtcbiAgICB0aGlzLm1vZGVscyA9IEFkYXB0ZXIubW9kZWxzKHRoaXMuYWRhcHRlci5hbGlhcyk7XG4gICAgdGhpcy5pbml0aWFsaXplKCkudGhlbigoKSA9PlxuICAgICAgdGhpcy5sb2cudmVyYm9zZShcbiAgICAgICAgYERpc3BhdGNoIGluaXRpYWxpemVkIGZvciAke3RoaXMuYWRhcHRlciEuYWxpYXN9IGFkYXB0ZXJgXG4gICAgICApXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gU3RvcHMgb2JzZXJ2aW5nIGFuIGFkYXB0ZXJcbiAgICogQHN1bW1hcnkgRGlzY29ubmVjdHMgdGhpcyBkaXNwYXRjaCBmcm9tIGFuIGFkYXB0ZXJcbiAgICogQHBhcmFtIHtPYnNlcnZlcn0gb2JzZXJ2ZXIgLSBUaGUgYWRhcHRlciB0byBzdG9wIG9ic2VydmluZ1xuICAgKiBAcmV0dXJuIHt2b2lkfVxuICAgKi9cbiAgdW5PYnNlcnZlKG9ic2VydmVyOiBPYnNlcnZlcik6IHZvaWQge1xuICAgIGlmICh0aGlzLmFkYXB0ZXIgIT09IG9ic2VydmVyKVxuICAgICAgdGhyb3cgbmV3IFVuc3VwcG9ydGVkRXJyb3IoXG4gICAgICAgIFwiT25seSB0aGUgYWRhcHRlciB0aGF0IHdhcyB1c2VkIHRvIG9ic2VydmUgY2FuIGJlIHVub2JzZXJ2ZWRcIlxuICAgICAgKTtcbiAgICB0aGlzLmFkYXB0ZXIgPSB1bmRlZmluZWQ7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFVwZGF0ZXMgb2JzZXJ2ZXJzIGFib3V0IGEgZGF0YWJhc2UgZXZlbnRcbiAgICogQHN1bW1hcnkgTm90aWZpZXMgb2JzZXJ2ZXJzIGFib3V0IGEgY2hhbmdlIGluIHRoZSBkYXRhYmFzZVxuICAgKiBAcGFyYW0ge3N0cmluZ30gdGFibGUgLSBUaGUgbmFtZSBvZiB0aGUgdGFibGUgd2hlcmUgdGhlIGNoYW5nZSBvY2N1cnJlZFxuICAgKiBAcGFyYW0ge09wZXJhdGlvbktleXN8QnVsa0NydWRPcGVyYXRpb25LZXlzfHN0cmluZ30gZXZlbnQgLSBUaGUgdHlwZSBvZiBvcGVyYXRpb24gdGhhdCBvY2N1cnJlZFxuICAgKiBAcGFyYW0ge0V2ZW50SWRzfSBpZCAtIFRoZSBpZGVudGlmaWVyKHMpIG9mIHRoZSBhZmZlY3RlZCByZWNvcmQocylcbiAgICogQHJldHVybiB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiBhbGwgb2JzZXJ2ZXJzIGhhdmUgYmVlbiBub3RpZmllZFxuICAgKi9cbiAgYXN5bmMgdXBkYXRlT2JzZXJ2ZXJzKFxuICAgIHRhYmxlOiBzdHJpbmcsXG4gICAgZXZlbnQ6IE9wZXJhdGlvbktleXMgfCBCdWxrQ3J1ZE9wZXJhdGlvbktleXMgfCBzdHJpbmcsXG4gICAgaWQ6IEV2ZW50SWRzXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICghdGhpcy5hZGFwdGVyKVxuICAgICAgdGhyb3cgbmV3IEludGVybmFsRXJyb3IoYE5vIGFkYXB0ZXIgb2JzZXJ2ZWQgZm9yIGRpc3BhdGNoYCk7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHRoaXMuYWRhcHRlci5yZWZyZXNoKHRhYmxlLCBldmVudCwgaWQpO1xuICAgIH0gY2F0Y2ggKGU6IHVua25vd24pIHtcbiAgICAgIHRocm93IG5ldyBJbnRlcm5hbEVycm9yKGBGYWlsZWQgdG8gcmVmcmVzaCBkaXNwYXRjaDogJHtlfWApO1xuICAgIH1cbiAgfVxufVxuIl19