@decaf-ts/core
Version:
Core persistence module for the decaf framework
573 lines • 80.7 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Adapter = void 0;
const db_decorators_1 = require("@decaf-ts/db-decorators");
const decorator_validation_1 = require("@decaf-ts/decorator-validation");
const constants_1 = require("./constants.cjs");
const Repository_1 = require("./../repository/Repository.cjs");
const logging_1 = require("@decaf-ts/logging");
const utils_1 = require("./../utils/index.cjs");
const Dispatch_1 = require("./Dispatch.cjs");
const ObserverHandler_1 = require("./ObserverHandler.cjs");
decorator_validation_1.Decoration.setFlavourResolver((obj) => {
try {
return (Adapter.flavourOf(decorator_validation_1.Model.isModel(obj) ? obj.constructor : obj) ||
decorator_validation_1.DefaultFlavour);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
}
catch (e) {
return decorator_validation_1.DefaultFlavour;
}
});
/**
* @description Abstract base class for database adapters
* @summary Provides the foundation for all database adapters in the persistence layer. This class
* implements several interfaces to provide a consistent API for database operations, observer
* pattern support, and error handling. It manages adapter registration, CRUD operations, and
* observer notifications.
* @template Y - The underlying database driver type
* @template Q - The query object type used by the adapter
* @template F - The repository flags type
* @template C - The context type
* @param {Y} _native - The underlying database driver instance
* @param {string} flavour - The identifier for this adapter type
* @param {string} [_alias] - Optional alternative name for this adapter
* @class Adapter
* @example
* ```typescript
* // Implementing a concrete adapter
* class PostgresAdapter extends Adapter<pg.Client, pg.Query, PostgresFlags, PostgresContext> {
* constructor(client: pg.Client) {
* super(client, 'postgres');
* }
*
* async initialize() {
* // Set up the adapter
* await this.native.connect();
* }
*
* async create(tableName, id, model) {
* // Implementation for creating records
* const columns = Object.keys(model).join(', ');
* const values = Object.values(model);
* const placeholders = values.map((_, i) => `$${i+1}`).join(', ');
*
* const query = `INSERT INTO ${tableName} (${columns}) VALUES (${placeholders}) RETURNING *`;
* const result = await this.native.query(query, values);
* return result.rows[0];
* }
*
* // Other required method implementations...
* }
*
* // Using the adapter
* const pgClient = new pg.Client(connectionString);
* const adapter = new PostgresAdapter(pgClient);
* await adapter.initialize();
*
* // Set as the default adapter
* Adapter.setCurrent('postgres');
*
* // Perform operations
* const user = await adapter.create('users', 1, { name: 'John', email: 'john@example.com' });
* ```
* @mermaid
* classDiagram
* class Adapter {
* +Y native
* +string flavour
* +string alias
* +create(tableName, id, model)
* +read(tableName, id)
* +update(tableName, id, model)
* +delete(tableName, id)
* +observe(observer, filter)
* +unObserve(observer)
* +static current
* +static get(flavour)
* +static setCurrent(flavour)
* }
*
* class RawExecutor {
* +raw(query)
* }
*
* class Observable {
* +observe(observer, filter)
* +unObserve(observer)
* +updateObservers(table, event, id)
* }
*
* class Observer {
* +refresh(table, event, id)
* }
*
* class ErrorParser {
* +parseError(err)
* }
*
* Adapter --|> RawExecutor
* Adapter --|> Observable
* Adapter --|> Observer
* Adapter --|> ErrorParser
*/
class Adapter {
static { this._cache = {}; }
/**
* @description Logger accessor
* @summary Gets or initializes the logger for this adapter instance
* @return {Logger} The logger instance
*/
get log() {
if (!this.logger)
this.logger = logging_1.Logging.for(this);
return this.logger;
}
/**
* @description Gets the native database driver
* @summary Provides access to the underlying database driver instance
* @return {Y} The native database driver
*/
get native() {
return this._native;
}
/**
* @description Gets the adapter's alias or flavor name
* @summary Returns the alias if set, otherwise returns the flavor name
* @return {string} The adapter's identifier
*/
get alias() {
return this._alias || this.flavour;
}
/**
* @description Gets the repository constructor for this adapter
* @summary Returns the constructor for creating repositories that work with this adapter
* @template M - The model type
* @return {Constructor<Repository<M, Q, Adapter<Y, Q, F, C>, F, C>>} The repository constructor
*/
repository() {
return Repository_1.Repository;
}
/**
* @description Creates a new adapter instance
* @summary Initializes the adapter with the native driver and registers it in the adapter cache
*/
constructor(_native, flavour, _alias) {
this._native = _native;
this.flavour = flavour;
this._alias = _alias;
/**
* @description The context constructor for this adapter
* @summary Reference to the context class constructor used by this adapter
*/
this.Context = (db_decorators_1.Context);
if (this.flavour in Adapter._cache)
throw new db_decorators_1.InternalError(`${this.alias} persistence adapter ${this._alias ? `(${this.flavour}) ` : ""} already registered`);
Adapter._cache[this.alias] = this;
this.log.info(`Created ${this.alias} persistence adapter ${this._alias ? `(${this.flavour}) ` : ""} persistence adapter`);
if (!Adapter._current) {
this.log.verbose(`Defined ${this.alias} persistence adapter as current`);
Adapter._current = this;
}
}
/**
* @description Creates a new dispatch instance
* @summary Factory method that creates a dispatch instance for this adapter
* @return {Dispatch<Y>} A new dispatch instance
*/
Dispatch() {
return new Dispatch_1.Dispatch();
}
/**
* @description Creates a new observer handler
* @summary Factory method that creates an observer handler for this adapter
* @return {ObserverHandler} A new observer handler instance
*/
ObserverHandler() {
return new ObserverHandler_1.ObserverHandler();
}
/**
* @description Checks if an attribute name is reserved
* @summary Determines if a given attribute name is reserved and cannot be used as a column name
* @param {string} attr - The attribute name to check
* @return {boolean} True if the attribute is reserved, false otherwise
*/
isReserved(attr) {
return !attr;
}
/**
* @description Creates repository flags for an operation
* @summary Generates a set of flags that describe a database operation, combining default flags with overrides
* @template F - The Repository Flags type
* @template M - The model type
* @param {OperationKeys} operation - The type of operation being performed
* @param {Constructor<M>} model - The model constructor
* @param {Partial<F>} flags - Custom flag overrides
* @param {...any[]} args - Additional arguments
* @return {Promise<F>} The complete set of flags
*/
async flags(operation, model, flags,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
...args) {
return Object.assign({}, db_decorators_1.DefaultRepositoryFlags, flags, {
affectedTables: Repository_1.Repository.table(model),
writeOperation: operation !== db_decorators_1.OperationKeys.READ,
timestamp: new Date(),
operation: operation,
});
}
/**
* @description Creates a context for a database operation
* @summary Generates a context object that describes a database operation, used for tracking and auditing
* @template F - The Repository flags type
* @template M - The model type
* @param {OperationKeys.CREATE|OperationKeys.READ|OperationKeys.UPDATE|OperationKeys.DELETE} operation - The type of operation
* @param {Partial<F>} overrides - Custom flag overrides
* @param {Constructor<M>} model - The model constructor
* @param {...any[]} args - Additional arguments
* @return {Promise<C>} A promise that resolves to the context object
*/
async context(operation, overrides, model, ...args) {
const log = this.log.for(this.context);
log.debug(`Creating new context for ${operation} operation on ${model.name} model with flag overrides: ${JSON.stringify(overrides)}`);
const flags = await this.flags(operation, model, overrides, ...args);
log.debug(`Flags: ${JSON.stringify(flags)}`);
return new this.Context().accumulate(flags);
}
/**
* @description Prepares a model for persistence
* @summary Converts a model instance into a format suitable for database storage,
* handling column mapping and separating transient properties
* @template M - The model type
* @param {M} model - The model instance to prepare
* @param pk - The primary key property name
* @return The prepared data
*/
prepare(model, pk) {
const log = this.log.for(this.prepare);
log.silly(`Preparing model ${model.constructor.name} before persisting`);
const split = (0, db_decorators_1.modelToTransient)(model);
const result = Object.entries(split.model).reduce((accum, [key, val]) => {
if (typeof val === "undefined")
return accum;
const mappedProp = Repository_1.Repository.column(model, key);
if (this.isReserved(mappedProp))
throw new db_decorators_1.InternalError(`Property name ${mappedProp} is reserved`);
accum[mappedProp] = val;
return accum;
}, {});
if (model[constants_1.PersistenceKeys.METADATA]) {
log.silly(`Passing along persistence metadata for ${model[constants_1.PersistenceKeys.METADATA]}`);
Object.defineProperty(result, constants_1.PersistenceKeys.METADATA, {
enumerable: false,
writable: false,
configurable: true,
value: model[constants_1.PersistenceKeys.METADATA],
});
}
return {
record: result,
id: model[pk],
transient: split.transient,
};
}
/**
* @description Converts database data back into a model instance
* @summary Reconstructs a model instance from database data, handling column mapping
* and reattaching transient properties
* @template M - The model type
* @param obj - The database record
* @param {string|Constructor<M>} clazz - The model class or name
* @param pk - The primary key property name
* @param {string|number|bigint} id - The primary key value
* @param [transient] - Transient properties to reattach
* @return {M} The reconstructed model instance
*/
revert(obj, clazz, pk, id, transient) {
const log = this.log.for(this.revert);
const ob = {};
ob[pk] = id;
const m = (typeof clazz === "string" ? decorator_validation_1.Model.build(ob, clazz) : new clazz(ob));
log.silly(`Rebuilding model ${m.constructor.name} id ${id}`);
const metadata = obj[constants_1.PersistenceKeys.METADATA];
const result = Object.keys(m).reduce((accum, key) => {
if (key === pk)
return accum;
accum[key] = obj[Repository_1.Repository.column(accum, key)];
return accum;
}, m);
if (transient) {
log.verbose(`re-adding transient properties: ${Object.keys(transient).join(", ")}`);
Object.entries(transient).forEach(([key, val]) => {
if (key in result)
throw new db_decorators_1.InternalError(`Transient property ${key} already exists on model ${m.constructor.name}. should be impossible`);
result[key] = val;
});
}
if (metadata) {
log.silly(`Passing along ${this.flavour} persistence metadata for ${m.constructor.name} id ${id}: ${metadata}`);
Object.defineProperty(result, constants_1.PersistenceKeys.METADATA, {
enumerable: false,
configurable: false,
writable: false,
value: metadata,
});
}
return result;
}
/**
* @description Creates multiple records in the database
* @summary Inserts multiple records with the given IDs and data into the specified table
* @param {string} tableName - The name of the table to insert into
* @param id - The identifiers for the new records
* @param model - The data to insert for each record
* @param {...any[]} args - Additional arguments specific to the adapter implementation
* @return A promise that resolves to an array of created records
*/
async createAll(tableName, id, model, ...args) {
if (id.length !== model.length)
throw new db_decorators_1.InternalError("Ids and models must have the same length");
const log = this.log.for(this.createAll);
log.verbose(`Creating ${id.length} entries ${tableName} table`);
log.debug(`pks: ${id}`);
return Promise.all(id.map((i, count) => this.create(tableName, i, model[count], ...args)));
}
/**
* @description Retrieves multiple records from the database
* @summary Fetches multiple records with the given IDs from the specified table
* @param {string} tableName - The name of the table to read from
* @param id - The identifiers of the records to retrieve
* @param {...any[]} args - Additional arguments specific to the adapter implementation
* @return A promise that resolves to an array of retrieved records
*/
async readAll(tableName, id, ...args) {
const log = this.log.for(this.readAll);
log.verbose(`Reading ${id.length} entries ${tableName} table`);
log.debug(`pks: ${id}`);
return Promise.all(id.map((i) => this.read(tableName, i, ...args)));
}
/**
* @description Updates multiple records in the database
* @summary Modifies multiple existing records with the given IDs in the specified table
* @param {string} tableName - The name of the table to update
* @param {string[]|number[]} id - The identifiers of the records to update
* @param model - The new data for each record
* @param {...any[]} args - Additional arguments specific to the adapter implementation
* @return A promise that resolves to an array of updated records
*/
async updateAll(tableName, id, model, ...args) {
if (id.length !== model.length)
throw new db_decorators_1.InternalError("Ids and models must have the same length");
const log = this.log.for(this.updateAll);
log.verbose(`Updating ${id.length} entries ${tableName} table`);
log.debug(`pks: ${id}`);
return Promise.all(id.map((i, count) => this.update(tableName, i, model[count], ...args)));
}
/**
* @description Deletes multiple records from the database
* @summary Removes multiple records with the given IDs from the specified table
* @param {string} tableName - The name of the table to delete from
* @param id - The identifiers of the records to delete
* @param {...any[]} args - Additional arguments specific to the adapter implementation
* @return A promise that resolves to an array of deleted records
*/
async deleteAll(tableName, id, ...args) {
const log = this.log.for(this.createAll);
log.verbose(`Deleting ${id.length} entries ${tableName} table`);
log.debug(`pks: ${id}`);
return Promise.all(id.map((i) => this.delete(tableName, i, ...args)));
}
/**
* @description Registers an observer for database events
* @summary Adds an observer to be notified about database changes. The observer can optionally
* provide a filter function to receive only specific events.
* @param {Observer} observer - The observer to register
* @param {ObserverFilter} [filter] - Optional filter function to determine which events the observer receives
* @return {void}
*/
observe(observer, filter) {
if (!this.observerHandler)
Object.defineProperty(this, "observerHandler", {
value: this.ObserverHandler(),
writable: false,
});
this.observerHandler.observe(observer, filter);
this.log
.for(this.observe)
.verbose(`Registering new observer ${observer.toString()}`);
if (!this.dispatch) {
this.log.for(this.observe).info(`Creating dispatch for ${this.alias}`);
this.dispatch = this.Dispatch();
this.dispatch.observe(this);
}
}
/**
* @description Unregisters an observer
* @summary Removes a previously registered observer so it no longer receives database event notifications
* @param {Observer} observer - The observer to unregister
* @return {void}
*/
unObserve(observer) {
if (!this.observerHandler)
throw new db_decorators_1.InternalError("ObserverHandler not initialized. Did you register any observables?");
this.observerHandler.unObserve(observer);
this.log
.for(this.unObserve)
.verbose(`Observer ${observer.toString()} removed`);
}
/**
* @description Notifies all observers about a database event
* @summary Sends notifications to all registered observers about a change in the database,
* filtering based on each observer's filter function
* @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)
* @param {...any[]} args - Additional arguments to pass to the observers
* @return {Promise<void>} A promise that resolves when all observers have been notified
*/
async updateObservers(table, event, id, ...args) {
if (!this.observerHandler)
throw new db_decorators_1.InternalError("ObserverHandler not initialized. Did you register any observables?");
const log = this.log.for(this.updateObservers);
log.verbose(`Updating ${this.observerHandler.count()} observers for adapter ${this.alias}`);
await this.observerHandler.updateObservers(this.log, table, event, id, ...args);
}
/**
* @description Refreshes data based on a database event
* @summary Implementation of the Observer interface method that delegates to updateObservers
* @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)
* @param {...any[]} args - Additional arguments related to the event
* @return {Promise<void>} A promise that resolves when the refresh is complete
*/
async refresh(table, event, id, ...args) {
return this.updateObservers(table, event, id, ...args);
}
/**
* @description Gets a string representation of the adapter
* @summary Returns a human-readable string identifying this adapter
* @return {string} A string representation of the adapter
*/
toString() {
return `${this.flavour} persistence Adapter`;
}
/**
* @description Gets the adapter flavor associated with a model
* @summary Retrieves the adapter flavor that should be used for a specific model class
* @template M - The model type
* @param {Constructor<M>} model - The model constructor
* @return {string} The adapter flavor name
*/
static flavourOf(model) {
return (Reflect.getMetadata(this.key(constants_1.PersistenceKeys.ADAPTER), model) ||
this.current.flavour);
}
/**
* @description Gets the current default adapter
* @summary Retrieves the adapter that is currently set as the default for operations
* @return {Adapter<any, any, any, any>} The current adapter
*/
static get current() {
if (!Adapter._current)
throw new db_decorators_1.InternalError(`No persistence flavour set. Please initialize your adapter`);
return Adapter._current;
}
/**
* @description Gets an adapter by flavor
* @summary Retrieves a registered adapter by its flavor name
* @template Y - The database driver type
* @template Q - The query type
* @template C - The context type
* @template F - The repository flags type
* @param {string} flavour - The flavor name of the adapter to retrieve
* @return {Adapter<Y, Q, F, C> | undefined} The adapter instance or undefined if not found
*/
static get(flavour) {
if (flavour in this._cache)
return this._cache[flavour];
throw new db_decorators_1.InternalError(`No Adapter registered under ${flavour}.`);
}
/**
* @description Sets the current default adapter
* @summary Changes which adapter is used as the default for operations
* @param {string} flavour - The flavor name of the adapter to set as current
* @return {void}
*/
static setCurrent(flavour) {
const adapter = Adapter.get(flavour);
if (!adapter)
throw new db_decorators_1.NotFoundError(`No persistence flavour ${flavour} registered`);
this._current = adapter;
}
/**
* @description Creates a metadata key
* @summary Generates a standardized metadata key for persistence-related metadata
* @param {string} key - The base key name
* @return {string} The formatted metadata key
*/
static key(key) {
return Repository_1.Repository.key(key);
}
/**
* @description Gets all models associated with an adapter flavor
* @summary Retrieves all model constructors that are configured to use a specific adapter flavor
* @template M - The model type
* @param {string} flavour - The adapter flavor to find models for
* @return An array of model constructors
*/
static models(flavour) {
try {
const registry = decorator_validation_1.Model.getRegistry();
const cache = registry.cache;
const managedModels = Object.values(cache)
.map((m) => {
let f = Reflect.getMetadata(Adapter.key(constants_1.PersistenceKeys.ADAPTER), m);
if (f && f === flavour)
return m;
if (!f) {
const repo = Reflect.getMetadata(Repository_1.Repository.key(db_decorators_1.DBKeys.REPOSITORY), m);
if (!repo)
return;
const repository = Repository_1.Repository.forModel(m);
f = Reflect.getMetadata(Adapter.key(constants_1.PersistenceKeys.ADAPTER), repository);
return f;
}
})
.filter((m) => !!m);
return managedModels;
}
catch (e) {
throw new db_decorators_1.InternalError(e);
}
}
}
exports.Adapter = Adapter;
__decorate([
(0, utils_1.final)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Object, Object, Object]),
__metadata("design:returntype", Promise)
], Adapter.prototype, "context", null);
__decorate([
(0, utils_1.final)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Function]),
__metadata("design:returntype", void 0)
], Adapter.prototype, "observe", null);
__decorate([
(0, utils_1.final)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], Adapter.prototype, "unObserve", null);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQWRhcHRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wZXJzaXN0ZW5jZS9BZGFwdGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7OztBQUFBLDJEQVlpQztBQUVqQyx5RUFPd0M7QUFJeEMsK0NBQThDO0FBQzlDLCtEQUFzRDtBQUl0RCwrQ0FBb0Q7QUFDcEQsZ0RBQWlDO0FBQ2pDLDZDQUFzQztBQUV0QywyREFBb0Q7QUFFcEQsaUNBQVUsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLEdBQVcsRUFBRSxFQUFFO0lBQzVDLElBQUksQ0FBQztRQUNILE9BQU8sQ0FDTCxPQUFPLENBQUMsU0FBUyxDQUFDLDRCQUFLLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBRSxHQUFXLENBQUM7WUFDdEUscUNBQWMsQ0FDZixDQUFDO1FBQ0YsNkRBQTZEO0lBQy9ELENBQUM7SUFBQyxPQUFPLENBQVUsRUFBRSxDQUFDO1FBQ3BCLE9BQU8scUNBQWMsQ0FBQztJQUN4QixDQUFDO0FBQ0gsQ0FBQyxDQUFDLENBQUM7QUFFSDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTJGRztBQUNILE1BQXNCLE9BQU87YUFTWixXQUFNLEdBQWdELEVBQUUsQUFBbEQsQ0FBbUQ7SUFReEU7Ozs7T0FJRztJQUNILElBQWMsR0FBRztRQUNmLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTTtZQUFFLElBQUksQ0FBQyxNQUFNLEdBQUcsaUJBQU8sQ0FBQyxHQUFHLENBQUMsSUFBVyxDQUFDLENBQUM7UUFDekQsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDO0lBQ3JCLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsSUFBSSxNQUFNO1FBQ1IsT0FBTyxJQUFJLENB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QVUsQ0FDWCxDQUFDO29CQUNGLE9BQU8sQ0FBQyxDQUFDO2dCQUNYLENBQUM7WUFDSCxDQUFDLENBQUM7aUJBQ0QsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDdEIsT0FBTyxhQUFhLENBQUM7UUFDdkIsQ0FBQztRQUFDLE9BQU8sQ0FBTSxFQUFFLENBQUM7WUFDaEIsTUFBTSxJQUFJLDZCQUFhLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDN0IsQ0FBQztJQUNILENBQUM7O0FBNXFCSCwwQkE2cUJDO0FBcmZPO0lBREwsSUFBQSxhQUFLLEdBQUU7Ozs7c0NBa0JQO0FBeVJEO0lBREMsSUFBQSxhQUFLLEdBQUU7Ozs7c0NBZ0JQO0FBU0Q7SUFEQyxJQUFBLGFBQUssR0FBRTs7Ozt3Q0FVUCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIEJhc2VFcnJvcixcbiAgREJLZXlzLFxuICBJbnRlcm5hbEVycm9yLFxuICBOb3RGb3VuZEVycm9yLFxuICBDb250ZXh0LFxuICBPcGVyYXRpb25LZXlzLFxuICBSZXBvc2l0b3J5RmxhZ3MsXG4gIERlZmF1bHRSZXBvc2l0b3J5RmxhZ3MsXG4gIENvbnRleHR1YWwsXG4gIEJ1bGtDcnVkT3BlcmF0aW9uS2V5cyxcbiAgbW9kZWxUb1RyYW5zaWVudCxcbn0gZnJvbSBcIkBkZWNhZi10cy9kYi1kZWNvcmF0b3JzXCI7XG5pbXBvcnQgeyB0eXBlIE9ic2VydmVyIH0gZnJvbSBcIi4uL2ludGVyZmFjZXMvT2JzZXJ2ZXJcIjtcbmltcG9ydCB7XG4gIHR5cGUgQ29uc3RydWN0b3IsXG4gIERlY29yYXRpb24sXG4gIERlZmF1bHRGbGF2b3VyLFxuICBNb2RlbCxcbiAgTW9kZWxDb25zdHJ1Y3RvcixcbiAgTW9kZWxSZWdpc3RyeSxcbn0gZnJvbSBcIkBkZWNhZi10cy9kZWNvcmF0b3ItdmFsaWRhdGlvblwiO1xuaW1wb3J0IHsgU2VxdWVuY2VPcHRpb25zIH0gZnJvbSBcIi4uL2ludGVyZmFjZXMvU2VxdWVuY2VPcHRpb25zXCI7XG5pbXBvcnQgeyBSYXdFeGVjdXRvciB9IGZyb20gXCIuLi9pbnRlcmZhY2VzL1Jhd0V4ZWN1dG9yXCI7XG5pbXBvcnQgeyBPYnNlcnZhYmxlIH0gZnJvbSBcIi4uL2ludGVyZmFjZXMvT2JzZXJ2YWJsZVwiO1xuaW1wb3J0IHsgUGVyc2lzdGVuY2VLZXlzIH0gZnJvbSBcIi4vY29uc3RhbnRzXCI7XG5pbXBvcnQgeyBSZXBvc2l0b3J5IH0gZnJvbSBcIi4uL3JlcG9zaXRvcnkvUmVwb3NpdG9yeVwiO1xuaW1wb3J0IHsgU2VxdWVuY2UgfSBmcm9tIFwiLi9TZXF1ZW5jZVwiO1xuaW1wb3J0IHsgRXJyb3JQYXJzZXIgfSBmcm9tIFwiLi4vaW50ZXJmYWNlc1wiO1xuaW1wb3J0IHsgU3RhdGVtZW50IH0gZnJvbSBcIi4uL3F1ZXJ5L1N0YXRlbWVudFwiO1xuaW1wb3J0IHsgTG9nZ2VyLCBMb2dnaW5nIH0gZnJvbSBcIkBkZWNhZi10cy9sb2dnaW5nXCI7XG5pbXBvcnQgeyBmaW5hbCB9IGZyb20gXCIuLi91dGlsc1wiO1xuaW1wb3J0IHsgRGlzcGF0Y2ggfSBmcm9tIFwiLi9EaXNwYXRjaFwiO1xuaW1wb3J0IHsgdHlwZSBFdmVudElkcywgdHlwZSBPYnNlcnZlckZpbHRlciB9IGZyb20gXCIuL3R5cGVzXCI7XG5pbXBvcnQgeyBPYnNlcnZlckhhbmRsZXIgfSBmcm9tIFwiLi9PYnNlcnZlckhhbmRsZXJcIjtcblxuRGVjb3JhdGlvbi5zZXRGbGF2b3VyUmVzb2x2ZXIoKG9iajogb2JqZWN0KSA9PiB7XG4gIHRyeSB7XG4gICAgcmV0dXJuIChcbiAgICAgIEFkYXB0ZXIuZmxhdm91ck9mKE1vZGVsLmlzTW9kZWwob2JqKSA/IG9iai5jb25zdHJ1Y3RvciA6IChvYmogYXMgYW55KSkgfHxcbiAgICAgIERlZmF1bHRGbGF2b3VyXG4gICAgKTtcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXVudXNlZC12YXJzXG4gIH0gY2F0Y2ggKGU6IHVua25vd24pIHtcbiAgICByZXR1cm4gRGVmYXVsdEZsYXZvdXI7XG4gIH1cbn0pO1xuXG4vKipcbiAqIEBkZXNjcmlwdGlvbiBBYnN0cmFjdCBiYXNlIGNsYXNzIGZvciBkYXRhYmFzZSBhZGFwdGVyc1xuICogQHN1bW1hcnkgUHJvdmlkZXMgdGhlIGZvdW5kYXRpb24gZm9yIGFsbCBkYXRhYmFzZSBhZGFwdGVycyBpbiB0aGUgcGVyc2lzdGVuY2UgbGF5ZXIuIFRoaXMgY2xhc3NcbiAqIGltcGxlbWVudHMgc2V2ZXJhbCBpbnRlcmZhY2VzIHRvIHByb3ZpZGUgYSBjb25zaXN0ZW50IEFQSSBmb3IgZGF0YWJhc2Ugb3BlcmF0aW9ucywgb2JzZXJ2ZXJcbiAqIHBhdHRlcm4gc3VwcG9ydCwgYW5kIGVycm9yIGhhbmRsaW5nLiBJdCBtYW5hZ2VzIGFkYXB0ZXIgcmVnaXN0cmF0aW9uLCBDUlVEIG9wZXJhdGlvbnMsIGFuZFxuICogb2JzZXJ2ZXIgbm90aWZpY2F0aW9ucy5cbiAqIEB0ZW1wbGF0ZSBZIC0gVGhlIHVuZGVybHlpbmcgZGF0YWJhc2UgZHJpdmVyIHR5cGVcbiAqIEB0ZW1wbGF0ZSBRIC0gVGhlIHF1ZXJ5IG9iamVjdCB0eXBlIHVzZWQgYnkgdGhlIGFkYXB0ZXJcbiAqIEB0ZW1wbGF0ZSBGIC0gVGhlIHJlcG9zaXRvcnkgZmxhZ3MgdHlwZVxuICogQHRlbXBsYXRlIEMgLSBUaGUgY29udGV4dCB0eXBlXG4gKiBAcGFyYW0ge1l9IF9uYXRpdmUgLSBUaGUgdW5kZXJseWluZyBkYXRhYmFzZSBkcml2ZXIgaW5zdGFuY2VcbiAqIEBwYXJhbSB7c3RyaW5nfSBmbGF2b3VyIC0gVGhlIGlkZW50aWZpZXIgZm9yIHRoaXMgYWRhcHRlciB0eXBlXG4gKiBAcGFyYW0ge3N0cmluZ30gW19hbGlhc10gLSBPcHRpb25hbCBhbHRlcm5hdGl2ZSBuYW1lIGZvciB0aGlzIGFkYXB0ZXJcbiAqIEBjbGFzcyBBZGFwdGVyXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogLy8gSW1wbGVtZW50aW5nIGEgY29uY3JldGUgYWRhcHRlclxuICogY2xhc3MgUG9zdGdyZXNBZGFwdGVyIGV4dGVuZHMgQWRhcHRlcjxwZy5DbGllbnQsIHBnLlF1ZXJ5LCBQb3N0Z3Jlc0ZsYWdzLCBQb3N0Z3Jlc0NvbnRleHQ+IHtcbiAqICAgY29uc3RydWN0b3IoY2xpZW50OiBwZy5DbGllbnQpIHtcbiAqICAgICBzdXBlcihjbGllbnQsICdwb3N0Z3JlcycpO1xuICogICB9XG4gKlxuICogICBhc3luYyBpbml0aWFsaXplKCkge1xuICogICAgIC8vIFNldCB1cCB0aGUgYWRhcHRlclxuICogICAgIGF3YWl0IHRoaXMubmF0aXZlLmNvbm5lY3QoKTtcbiAqICAgfVxuICpcbiAqICAgYXN5bmMgY3JlYXRlKHRhYmxlTmFtZSwgaWQsIG1vZGVsKSB7XG4gKiAgICAgLy8gSW1wbGVtZW50YXRpb24gZm9yIGNyZWF0aW5nIHJlY29yZHNcbiAqICAgICBjb25zdCBjb2x1bW5zID0gT2JqZWN0LmtleXMobW9kZWwpLmpvaW4oJywgJyk7XG4gKiAgICAgY29uc3QgdmFsdWVzID0gT2JqZWN0LnZhbHVlcyhtb2RlbCk7XG4gKiAgICAgY29uc3QgcGxhY2Vob2xkZXJzID0gdmFsdWVzLm1hcCgoXywgaSkgPT4gYCQke2krMX1gKS5qb2luKCcsICcpO1xuICpcbiAqICAgICBjb25zdCBxdWVyeSA9IGBJTlNFUlQgSU5UTyAke3RhYmxlTmFtZX0gKCR7Y29sdW1uc30pIFZBTFVFUyAoJHtwbGFjZWhvbGRlcnN9KSBSRVRVUk5JTkcgKmA7XG4gKiAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5uYXRpdmUucXVlcnkocXVlcnksIHZhbHVlcyk7XG4gKiAgICAgcmV0dXJuIHJlc3VsdC5yb3dzWzBdO1xuICogICB9XG4gKlxuICogICAvLyBPdGhlciByZXF1aXJlZCBtZXRob2QgaW1wbGVtZW50YXRpb25zLi4uXG4gKiB9XG4gKlxuICogLy8gVXNpbmcgdGhlIGFkYXB0ZXJcbiAqIGNvbnN0IHBnQ2xpZW50ID0gbmV3IHBnLkNsaWVudChjb25uZWN0aW9uU3RyaW5nKTtcbiAqIGNvbnN0IGFkYXB0ZXIgPSBuZXcgUG9zdGdyZXNBZGFwdGVyKHBnQ2xpZW50KTtcbiAqIGF3YWl0IGFkYXB0ZXIuaW5pdGlhbGl6ZSgpO1xuICpcbiAqIC8vIFNldCBhcyB0aGUgZGVmYXVsdCBhZGFwdGVyXG4gKiBBZGFwdGVyLnNldEN1cnJlbnQoJ3Bvc3RncmVzJyk7XG4gKlxuICogLy8gUGVyZm9ybSBvcGVyYXRpb25zXG4gKiBjb25zdCB1c2VyID0gYXdhaXQgYWRhcHRlci5jcmVhdGUoJ3VzZXJzJywgMSwgeyBuYW1lOiAnSm9obicsIGVtYWlsOiAnam9obkBleGFtcGxlLmNvbScgfSk7XG4gKiBgYGBcbiAqIEBtZXJtYWlkXG4gKiBjbGFzc0RpYWdyYW1cbiAqICAgY2xhc3MgQWRhcHRlciB7XG4gKiAgICAgK1kgbmF0aXZlXG4gKiAgICAgK3N0cmluZyBmbGF2b3VyXG4gKiAgICAgK3N0cmluZyBhbGlhc1xuICogICAgICtjcmVhdGUodGFibGVOYW1lLCBpZCwgbW9kZWwpXG4gKiAgICAgK3JlYWQodGFibGVOYW1lLCBpZClcbiAqICAgICArdXBkYXRlKHRhYmxlTmFtZSwgaWQsIG1vZGVsKVxuICogICAgICtkZWxldGUodGFibGVOYW1lLCBpZClcbiAqICAgICArb2JzZXJ2ZShvYnNlcnZlciwgZmlsdGVyKVxuICogICAgICt1bk9ic2VydmUob2JzZXJ2ZXIpXG4gKiAgICAgK3N0YXRpYyBjdXJyZW50XG4gKiAgICAgK3N0YXRpYyBnZXQoZmxhdm91cilcbiAqICAgICArc3RhdGljIHNldEN1cnJlbnQoZmxhdm91cilcbiAqICAgfVxuICpcbiAqICAgY2xhc3MgUmF3RXhlY3V0b3Ige1xuICogICAgICtyYXcocXVlcnkpXG4gKiAgIH1cbiAqXG4gKiAgIGNsYXNzIE9ic2VydmFibGUge1xuICogICAgICtvYnNlcnZlKG9ic2VydmVyLCBmaWx0ZXIpXG4gKiAgICAgK3VuT2JzZXJ2ZShvYnNlcnZlcilcbiAqICAgICArdXBkYXRlT2JzZXJ2ZXJzKHRhYmxlLCBldmVudCwgaWQpXG4gKiAgIH1cbiAqXG4gKiAgIGNsYXNzIE9ic2VydmVyIHtcbiAqICAgICArcmVmcmVzaCh0YWJsZSwgZXZlbnQsIGlkKVxuICogICB9XG4gKlxuICogICBjbGFzcyBFcnJvclBhcnNlciB7XG4gKiAgICAgK3BhcnNlRXJyb3IoZXJyKVxuICogICB9XG4gKlxuICogICBBZGFwdGVyIC0tfD4gUmF3RXhlY3V0b3JcbiAqICAgQWRhcHRlciAtLXw+IE9ic2VydmFibGVcbiAqICAgQWRhcHRlciAtLXw+IE9ic2VydmVyXG4gKiAgIEFkYXB0ZXIgLS18PiBFcnJvclBhcnNlclxuICovXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgQWRhcHRlcjxcbiAgICBZLFxuICAgIFEsXG4gICAgRiBleHRlbmRzIFJlcG9zaXRvcnlGbGFncyxcbiAgICBDIGV4dGVuZHMgQ29udGV4dDxGPixcbiAgPlxuICBpbXBsZW1lbnRzIFJhd0V4ZWN1dG9yPFE+LCBDb250ZXh0dWFsPEYsIEM+LCBPYnNlcnZhYmxlLCBPYnNlcnZlciwgRXJyb3JQYXJzZXJcbntcbiAgcHJpdmF0ZSBzdGF0aWMgX2N1cnJlbnQ6IEFkYXB0ZXI8YW55LCBhbnksIGFueSwgYW55PjtcbiAgcHJpdmF0ZSBzdGF0aWMgX2NhY2hlOiBSZWNvcmQ8c3RyaW5nLCBBZGFwdGVyPGFueSwgYW55LCBhbnksIGFueT4+ID0ge307XG5cbiAgcHJpdmF0ZSBsb2dnZXIhOiBMb2dnZXI7XG5cbiAgcHJvdGVjdGVkIGRpc3BhdGNoPzogRGlzcGF0Y2g8WT47XG5cbiAgcHJvdGVjdGVkIHJlYWRvbmx5IG9ic2VydmVySGFuZGxlcj86IE9ic2VydmVySGFuZGxlcjtcblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIExvZ2dlciBhY2Nlc3NvclxuICAgKiBAc3VtbWFyeSBHZXRzIG9yIGluaXRpYWxpemVzIHRoZSBsb2dnZXIgZm9yIHRoaXMgYWRhcHRlciBpbnN0YW5jZVxuICAgKiBAcmV0dXJuIHtMb2dnZXJ9IFRoZSBsb2dnZXIgaW5zdGFuY2VcbiAgICovXG4gIHByb3RlY3RlZCBnZXQgbG9nKCkge1xuICAgIGlmICghdGhpcy5sb2dnZXIpIHRoaXMubG9nZ2VyID0gTG9nZ2luZy5mb3IodGhpcyBhcyBhbnkpO1xuICAgIHJldHVybiB0aGlzLmxvZ2dlcjtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gR2V0cyB0aGUgbmF0aXZlIGRhdGFiYXNlIGRyaXZlclxuICAgKiBAc3VtbWFyeSBQcm92aWRlcyBhY2Nlc3MgdG8gdGhlIHVuZGVybHlpbmcgZGF0YWJhc2UgZHJpdmVyIGluc3RhbmNlXG4gICAqIEByZXR1cm4ge1l9IFRoZSBuYXRpdmUgZGF0YWJhc2UgZHJpdmVyXG4gICAqL1xuICBnZXQgbmF0aXZlKCkge1xuICAgIHJldHVybiB0aGlzLl9uYXRpdmU7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEdldHMgdGhlIGFkYXB0ZXIncyBhbGlhcyBvciBmbGF2b3IgbmFtZVxuICAgKiBAc3VtbWFyeSBSZXR1cm5zIHRoZSBhbGlhcyBpZiBzZXQsIG90aGVyd2lzZSByZXR1cm5zIHRoZSBmbGF2b3IgbmFtZVxuICAgKiBAcmV0dXJuIHtzdHJpbmd9IFRoZSBhZGFwdGVyJ3MgaWRlbnRpZmllclxuICAgKi9cbiAgZ2V0IGFsaWFzKCkge1xuICAgIHJldHVybiB0aGlzLl9hbGlhcyB8fCB0aGlzLmZsYXZvdXI7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEdldHMgdGhlIHJlcG9zaXRvcnkgY29uc3RydWN0b3IgZm9yIHRoaXMgYWRhcHRlclxuICAgKiBAc3VtbWFyeSBSZXR1cm5zIHRoZSBjb25zdHJ1Y3RvciBmb3IgY3JlYXRpbmcgcmVwb3NpdG9yaWVzIHRoYXQgd29yayB3aXRoIHRoaXMgYWRhcHRlclxuICAgKiBAdGVtcGxhdGUgTSAtIFRoZSBtb2RlbCB0eXBlXG4gICAqIEByZXR1cm4ge0NvbnN0cnVjdG9yPFJlcG9zaXRvcnk8TSwgUSwgQWRhcHRlcjxZLCBRLCBGLCBDPiwgRiwgQz4+fSBUaGUgcmVwb3NpdG9yeSBjb25zdHJ1Y3RvclxuICAgKi9cbiAgcmVwb3NpdG9yeTxNIGV4dGVuZHMgTW9kZWw8dHJ1ZSB8IGZhbHNlPj4oKTogQ29uc3RydWN0b3I8XG4gICAgUmVwb3NpdG9yeTxNLCBRLCBBZGFwdGVyPFksIFEsIEYsIEM+LCBGLCBDPlxuICA+IHtcbiAgICByZXR1cm4gUmVwb3NpdG9yeTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQ3JlYXRlcyBhIG5ldyBhZGFwdGVyIGluc3RhbmNlXG4gICAqIEBzdW1tYXJ5IEluaXRpYWxpemVzIHRoZSBhZGFwdGVyIHdpdGggdGhlIG5hdGl2ZSBkcml2ZXIgYW5kIHJlZ2lzdGVycyBpdCBpbiB0aGUgYWRhcHRlciBjYWNoZVxuICAgKi9cbiAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgX25hdGl2ZTogWSxcbiAgICByZWFkb25seSBmbGF2b3VyOiBzdHJpbmcsXG4gICAgcHJpdmF0ZSByZWFkb25seSBfYWxpYXM/OiBzdHJpbmdcbiAgKSB7XG4gICAgaWYgKHRoaXMuZmxhdm91ciBpbiBBZGFwdGVyLl9jYWNoZSlcbiAgICAgIHRocm93IG5ldyBJbnRlcm5hbEVycm9yKFxuICAgICAgICBgJHt0aGlzLmFsaWFzfSBwZXJzaXN0ZW5jZSBhZGFwdGVyICR7dGhpcy5fYWxpYXMgPyBgKCR7dGhpcy5mbGF2b3VyfSkgYCA6IFwiXCJ9IGFscmVhZHkgcmVnaXN0ZXJlZGBcbiAgICAgICk7XG4gICAgQWRhcHRlci5fY2FjaGVbdGhpcy5hbGlhc10gPSB0aGlzO1xuICAgIHRoaXMubG9nLmluZm8oXG4gICAgICBgQ3JlYXRlZCAke3RoaXMuYWxpYXN9IHBlcnNpc3RlbmNlIGFkYXB0ZXIgJHt0aGlzLl9hbGlhcyA/IGAoJHt0aGlzLmZsYXZvdXJ9KSBgIDogXCJcIn0gcGVyc2lzdGVuY2UgYWRhcHRlcmBcbiAgICApO1xuICAgIGlmICghQWRhcHRlci5fY3VycmVudCkge1xuICAgICAgdGhpcy5sb2cudmVyYm9zZShgRGVmaW5lZCAke3RoaXMuYWxpYXN9IHBlcnNpc3RlbmNlIGFkYXB0ZXIgYXMgY3VycmVudGApO1xuICAgICAgQWRhcHRlci5fY3VycmVudCA9IHRoaXM7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBDcmVhdGVzIGEgbmV3IHN0YXRlbWVudCBidWlsZGVyIGZvciBhIG1vZGVsXG4gICAqIEBzdW1tYXJ5IFJldHVybnMgYSBzdGF0ZW1lbnQgYnVpbGRlciB0aGF0IGNhbiBiZSB1c2VkIHRvIGNvbnN0cnVjdCBxdWVyaWVzIGZvciBhIHNwZWNpZmljIG1vZGVsXG4gICAqIEB0ZW1wbGF0ZSBNIC0gVGhlIG1vZGVsIHR5cGVcbiAgICogQHJldHVybiB7U3RhdGVtZW50fSBBIHN0YXRlbWVudCBidWlsZGVyIGZvciB0aGUgbW9kZWxcbiAgICovXG4gIGFic3RyYWN0IFN0YXRlbWVudDxNIGV4dGVuZHMgTW9kZWw+KCk6IFN0YXRlbWVudDxRLCB