UNPKG

@decaf-ts/core

Version:

Core persistence module for the decaf framework

468 lines 59 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RamAdapter = void 0; const RamStatement_1 = require("./RamStatement.cjs"); const RamContext_1 = require("./RamContext.cjs"); const Repository_1 = require("./../repository/Repository.cjs"); const persistence_1 = require("./../persistence/index.cjs"); const transactional_decorators_1 = require("@decaf-ts/transactional-decorators"); const decorator_validation_1 = require("@decaf-ts/decorator-validation"); const db_decorators_1 = require("@decaf-ts/db-decorators"); const RamSequence_1 = require("./RamSequence.cjs"); const handlers_1 = require("./handlers.cjs"); const constants_1 = require("./constants.cjs"); persistence_1.Adapter.setCurrent(constants_1.RamFlavour); /** * @description In-memory adapter for data persistence * @summary The RamAdapter provides an in-memory implementation of the persistence layer. * It stores data in JavaScript Maps and provides CRUD operations and query capabilities. * This adapter is useful for testing, prototyping, and applications that don't require * persistent storage across application restarts. * @class RamAdapter * @category Ram * @example * ```typescript * // Create a new RAM adapter * const adapter = new RamAdapter('myRamAdapter'); * * // Create a repository for a model * const userRepo = new (adapter.repository<User>())(User, adapter); * * // Perform CRUD operations * const user = new User({ name: 'John', email: 'john@example.com' }); * await userRepo.create(user); * const retrievedUser = await userRepo.findById(user.id); * ``` * @mermaid * sequenceDiagram * participant Client * participant Repository * participant RamAdapter * participant Storage as In-Memory Storage * * Client->>Repository: create(model) * Repository->>RamAdapter: create(tableName, id, model) * RamAdapter->>RamAdapter: lock.acquire() * RamAdapter->>Storage: set(id, model) * RamAdapter->>RamAdapter: lock.release() * RamAdapter-->>Repository: model * Repository-->>Client: model * * Client->>Repository: findById(id) * Repository->>RamAdapter: read(tableName, id) * RamAdapter->>Storage: get(id) * Storage-->>RamAdapter: model * RamAdapter-->>Repository: model * Repository-->>Client: model */ class RamAdapter extends persistence_1.Adapter { constructor(conf = {}, alias) { super(conf, constants_1.RamFlavour, alias); this.Context = RamContext_1.RamContext; this.indexes = {}; this.lock = new transactional_decorators_1.Lock(); } /** * @description Gets the repository constructor for a model * @summary Returns a constructor for creating repositories that work with the specified model type. * This method overrides the base implementation to provide RAM-specific repository functionality. * @template M - The model type for the repository * @return {Constructor<RamRepository<M>>} A constructor for creating RAM repositories */ repository() { return super.repository(); } /** * @description Creates operation flags with UUID * @summary Extends the base flags with a UUID for user identification. * This method ensures that all operations have a unique identifier for tracking purposes. * @template M - The model type for the operation * @param {OperationKeys} operation - The type of operation being performed * @param {Constructor<M>} model - The model constructor * @param {Partial<RamFlags>} flags - Partial flags to be extended * @return {Promise<RamFlags>} Complete flags with UUID */ async flags(operation, model, flags) { return Object.assign(await super.flags(operation, model, flags), { UUID: this.config.user || "" + Date.now(), }); } /** * @description Indexes models in the RAM adapter * @summary A no-op indexing method for the RAM adapter. * Since RAM adapter doesn't require explicit indexing, this method simply resolves immediately. * @param models - Models to be indexed (unused) * @return {Promise<any>} A promise that resolves when indexing is complete */ // eslint-disable-next-line @typescript-eslint/no-unused-vars async index(...models) { return Promise.resolve(undefined); } /** * @description Prepares a model for storage * @summary Converts a model instance to a format suitable for storage in the RAM adapter. * This method extracts the primary key and creates a record without the primary key field. * @template M - The model type being prepared * @param {M} model - The model instance to prepare * @param pk - The primary key property name * @return Object containing the record and ID */ prepare(model, pk) { const prepared = super.prepare(model, pk); delete prepared.record[pk]; return prepared; } /** * @description Converts a stored record back to a model instance * @summary Reconstructs a model instance from a stored record by adding back the primary key. * This method is the inverse of the prepare method. * @template M - The model type to revert to * @param {Record<string, any>} obj - The stored record * @param {string | Constructor<M>} clazz - The model class or name * @param pk - The primary key property name * @param {string | number} id - The primary key value * @return {M} The reconstructed model instance */ revert(obj, clazz, pk, id) { const res = super.revert(obj, clazz, pk, id); return res; } /** * @description Creates a new record in the in-memory storage * @summary Stores a new record in the specified table with the given ID. * This method acquires a lock to ensure thread safety, creates the table if it doesn't exist, * checks for conflicts, and stores the model. * @param {string} tableName - The name of the table to store the record in * @param {string | number} id - The unique identifier for the record * @param {Record<string, any>} model - The record data to store * @return {Promise<Record<string, any>>} A promise that resolves to the stored record * @mermaid * sequenceDiagram * participant Caller * participant RamAdapter * participant Storage as In-Memory Storage * * Caller->>RamAdapter: create(tableName, id, model) * RamAdapter->>RamAdapter: lock.acquire() * RamAdapter->>Storage: has(tableName) * alt Table doesn't exist * RamAdapter->>Storage: set(tableName, new Map()) * end * RamAdapter->>Storage: has(id) * alt Record exists * RamAdapter-->>Caller: throw ConflictError * end * RamAdapter->>Storage: set(id, model) * RamAdapter->>RamAdapter: lock.release() * RamAdapter-->>Caller: model */ async create(tableName, id, model) { await this.lock.acquire(); if (!this.client.has(tableName)) this.client.set(tableName, new Map()); if (this.client.get(tableName) && this.client.get(tableName)?.has(id)) throw new db_decorators_1.ConflictError(`Record with id ${id} already exists in table ${tableName}`); this.client.get(tableName)?.set(id, model); this.lock.release(); return model; } /** * @description Retrieves a record from in-memory storage * @summary Fetches a record with the specified ID from the given table. * This method checks if the table and record exist and throws appropriate errors if not. * @param {string} tableName - The name of the table to retrieve from * @param {string | number} id - The unique identifier of the record to retrieve * @return {Promise<Record<string, any>>} A promise that resolves to the retrieved record * @mermaid * sequenceDiagram * participant Caller * participant RamAdapter * participant Storage as In-Memory Storage * * Caller->>RamAdapter: read(tableName, id) * RamAdapter->>Storage: has(tableName) * alt Table doesn't exist * RamAdapter-->>Caller: throw NotFoundError * end * RamAdapter->>Storage: has(id) * alt Record doesn't exist * RamAdapter-->>Caller: throw NotFoundError * end * RamAdapter->>Storage: get(id) * Storage-->>RamAdapter: record * RamAdapter-->>Caller: record */ async read(tableName, id) { if (!this.client.has(tableName)) throw new db_decorators_1.NotFoundError(`Table ${tableName} not found`); if (!this.client.get(tableName)?.has(id)) throw new db_decorators_1.NotFoundError(`Record with id ${id} not found in table ${tableName}`); return this.client.get(tableName)?.get(id); } /** * @description Updates an existing record in the in-memory storage * @summary Updates a record with the specified ID in the given table. * This method acquires a lock to ensure thread safety, checks if the table and record exist, * and updates the record with the new data. * @param {string} tableName - The name of the table containing the record * @param {string | number} id - The unique identifier of the record to update * @param {Record<string, any>} model - The new record data * @return {Promise<Record<string, any>>} A promise that resolves to the updated record * @mermaid * sequenceDiagram * participant Caller * participant RamAdapter * participant Storage as In-Memory Storage * * Caller->>RamAdapter: update(tableName, id, model) * RamAdapter->>RamAdapter: lock.acquire() * RamAdapter->>Storage: has(tableName) * alt Table doesn't exist * RamAdapter-->>Caller: throw NotFoundError * end * RamAdapter->>Storage: has(id) * alt Record doesn't exist * RamAdapter-->>Caller: throw NotFoundError * end * RamAdapter->>Storage: set(id, model) * RamAdapter->>RamAdapter: lock.release() * RamAdapter-->>Caller: model */ async update(tableName, id, model) { await this.lock.acquire(); if (!this.client.has(tableName)) throw new db_decorators_1.NotFoundError(`Table ${tableName} not found`); if (!this.client.get(tableName)?.has(id)) throw new db_decorators_1.NotFoundError(`Record with id ${id} not found in table ${tableName}`); this.client.get(tableName)?.set(id, model); this.lock.release(); return model; } /** * @description Deletes a record from the in-memory storage * @summary Removes a record with the specified ID from the given table. * This method acquires a lock to ensure thread safety, checks if the table and record exist, * retrieves the record before deletion, and then removes it from storage. * @param {string} tableName - The name of the table containing the record * @param {string | number} id - The unique identifier of the record to delete * @return {Promise<Record<string, any>>} A promise that resolves to the deleted record * @mermaid * sequenceDiagram * participant Caller * participant RamAdapter * participant Storage as In-Memory Storage * * Caller->>RamAdapter: delete(tableName, id) * RamAdapter->>RamAdapter: lock.acquire() * RamAdapter->>Storage: has(tableName) * alt Table doesn't exist * RamAdapter-->>Caller: throw NotFoundError * end * RamAdapter->>Storage: has(id) * alt Record doesn't exist * RamAdapter-->>Caller: throw NotFoundError * end * RamAdapter->>Storage: get(id) * Storage-->>RamAdapter: record * RamAdapter->>Storage: delete(id) * RamAdapter->>RamAdapter: lock.release() * RamAdapter-->>Caller: record */ async delete(tableName, id) { await this.lock.acquire(); if (!this.client.has(tableName)) throw new db_decorators_1.NotFoundError(`Table ${tableName} not found`); if (!this.client.get(tableName)?.has(id)) throw new db_decorators_1.NotFoundError(`Record with id ${id} not found in table ${tableName}`); const natived = this.client.get(tableName)?.get(id); this.client.get(tableName)?.delete(id); this.lock.release(); return natived; } /** * @description Gets or creates a table in the in-memory storage * @summary Retrieves the Map representing a table for a given model or table name. * If the table doesn't exist, it creates a new one. This is a helper method used * by other methods to access the correct storage location. * @template M - The model type for the table * @param {string | Constructor<M>} from - The model class or table name * @return {Map<string | number, any> | undefined} The table Map or undefined */ tableFor(from) { if (typeof from === "string") from = decorator_validation_1.Model.get(from); const table = Repository_1.Repository.table(from); if (!this.client.has(table)) this.client.set(table, new Map()); return this.client.get(table); } /** * @description Executes a raw query against the in-memory storage * @summary Performs a query operation on the in-memory data store using the provided query specification. * This method supports filtering, sorting, pagination, and field selection. * @template R - The return type of the query * @param {RawRamQuery<any>} rawInput - The query specification * @return {Promise<R>} A promise that resolves to the query results * @mermaid * sequenceDiagram * participant Caller * participant RamAdapter * participant Storage as In-Memory Storage * * Caller->>RamAdapter: raw(rawInput) * RamAdapter->>RamAdapter: tableFor(from) * alt Table doesn't exist * RamAdapter-->>Caller: throw InternalError * end * RamAdapter->>RamAdapter: findPrimaryKey(new from()) * RamAdapter->>Storage: entries() * Storage-->>RamAdapter: entries * loop For each entry * RamAdapter->>RamAdapter: revert(r, from, id, pk) * end * alt Where condition exists * RamAdapter->>RamAdapter: result.filter(where) * end * alt Sort condition exists * RamAdapter->>RamAdapter: result.sort(sort) * end * alt Skip specified * RamAdapter->>RamAdapter: result.slice(skip) * end * alt Limit specified * RamAdapter->>RamAdapter: result.slice(0, limit) * end * alt Select fields specified * loop For each result * RamAdapter->>RamAdapter: Filter to selected fields * end * end * RamAdapter-->>Caller: result */ async raw(rawInput) { const { where, sort, limit, skip, from } = rawInput; let { select } = rawInput; const collection = this.tableFor(from); if (!collection) throw new db_decorators_1.InternalError(`Table ${from} not found in RamAdapter`); const { id, props } = (0, db_decorators_1.findPrimaryKey)(new from()); let result = Array.from(collection.entries()).map(([pk, r]) => this.revert(r, from, id, persistence_1.Sequence.parseValue(props.type, pk))); result = where ? result.filter(where) : result; if (sort) result = result.sort(sort); if (skip) result = result.slice(skip); if (limit) result = result.slice(0, limit); if (select) { select = Array.isArray(select) ? select : [select]; result = result.map((r) => Object.entries(r).reduce((acc, [key, val]) => { if (select.includes(key)) acc[key] = val; return acc; }, {})); } return result; } /** * @description Parses and converts errors to appropriate types * @summary Ensures that errors are of the correct type for consistent error handling. * If the error is already a BaseError, it's returned as is; otherwise, it's wrapped in an InternalError. * @template V - The expected error type, extending BaseError * @param {Error} err - The error to parse * @return {V} The parsed error of the expected type */ parseError(err) { if (err instanceof db_decorators_1.BaseError) return err; return new db_decorators_1.InternalError(err); } /** * @description Creates a new statement builder for queries * @summary Factory method that creates a new RamStatement instance for building queries. * This method allows for fluent query construction against the RAM adapter. * @template M - The model type for the statement * @return {RamStatement<M, any>} A new statement builder instance */ Statement() { return new RamStatement_1.RamStatement(this); } /** * @description Creates a new sequence for generating sequential IDs * @summary Factory method that creates a new RamSequence instance for ID generation. * This method provides a way to create auto-incrementing sequences for entity IDs. * @param {SequenceOptions} options - Configuration options for the sequence * @return {Promise<Sequence>} A promise that resolves to the new sequence instance */ async Sequence(options) { return new RamSequence_1.RamSequence(options, this); } // eslint-disable-next-line @typescript-eslint/no-unused-vars for(config, ...args) { if (!this.proxies) this.proxies = {}; const key = `${this.alias} - ${(0, decorator_validation_1.hashObj)(config)}`; if (key in this.proxies) return this.proxies[key]; const proxy = new Proxy(this, { get: (target, p, receiver) => { if (p === "_config") { const originalConf = Reflect.get(target, p, receiver); return Object.assign({}, originalConf, config); } return Reflect.get(target, p, receiver); }, }); this.proxies[key] = proxy; return proxy; } /** * @description Sets up RAM-specific decorations for model properties * @summary Configures decorations for createdBy and updatedBy fields in the RAM adapter. * This static method is called during initialization to set up handlers that automatically * populate these fields with the current user's UUID during create and update operations. * @return {void} * @mermaid * sequenceDiagram * participant RamAdapter * participant Decoration * participant Repository * * RamAdapter->>Repository: key(PersistenceKeys.CREATED_BY) * Repository-->>RamAdapter: createdByKey * RamAdapter->>Repository: key(PersistenceKeys.UPDATED_BY) * Repository-->>RamAdapter: updatedByKey * * RamAdapter->>Decoration: flavouredAs(RamFlavour) * Decoration-->>RamAdapter: DecoratorBuilder * RamAdapter->>Decoration: for(createdByKey) * RamAdapter->>Decoration: define(onCreate, propMetadata) * RamAdapter->>Decoration: apply() * * RamAdapter->>Decoration: flavouredAs(RamFlavour) * Decoration-->>RamAdapter: DecoratorBuilder * RamAdapter->>Decoration: for(updatedByKey) * RamAdapter->>Decoration: define(onCreate, propMetadata) * RamAdapter->>Decoration: apply() */ static decoration() { super.decoration(); const createdByKey = Repository_1.Repository.key(persistence_1.PersistenceKeys.CREATED_BY); const updatedByKey = Repository_1.Repository.key(persistence_1.PersistenceKeys.UPDATED_BY); decorator_validation_1.Decoration.flavouredAs(constants_1.RamFlavour) .for(createdByKey) .define((0, db_decorators_1.onCreate)(handlers_1.createdByOnRamCreateUpdate), (0, decorator_validation_1.propMetadata)(createdByKey, {})) .apply(); decorator_validation_1.Decoration.flavouredAs(constants_1.RamFlavour) .for(updatedByKey) .define((0, db_decorators_1.onCreateUpdate)(handlers_1.createdByOnRamCreateUpdate), (0, decorator_validation_1.propMetadata)(updatedByKey, {})) .apply(); } getClient() { return new Map(); } } exports.RamAdapter = RamAdapter; RamAdapter.decoration(); persistence_1.Adapter.setCurrent(constants_1.RamFlavour); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmFtQWRhcHRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9yYW0vUmFtQWRhcHRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFPQSxxREFBOEM7QUFDOUMsaURBQTBDO0FBQzFDLCtEQUFzRDtBQUN0RCw0REFBb0U7QUFFcEUsaUZBQTBEO0FBQzFELHlFQU13QztBQUN4QywyREFTaUM7QUFDakMsbURBQTRDO0FBQzVDLDZDQUF3RDtBQUN4RCwrQ0FBeUM7QUFFekMscUJBQU8sQ0FBQyxVQUFVLENBQUMsc0JBQVUsQ0FBQyxDQUFDO0FBRS9COzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0EwQ0c7QUFDSCxNQUFhLFVBQVcsU0FBUSxxQkFNL0I7SUFDQyxZQUFZLE9BQWtCLEVBQVMsRUFBRSxLQUFjO1FBQ3JELEtBQUssQ0FBQyxJQUFJLEVBQUUsc0JBQVUsRUFBRSxLQUFLLENBQUMsQ0FBQztRQW9DeEIsWUFBTyxHQUFHLHVCQUFVLENBQUM7UUFFdEIsWUFBTyxHQUdYLEVBQUUsQ0FBQztRQUVDLFNBQUksR0FBRyxJQUFJLCtCQUFJLEVBQUUsQ0FBQztJQTFDMUIsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNNLFVBQVU7UUFHakIsT0FBTyxLQUFLLENBQUMsVUFBVSxFQUFzQyxDQUFDO0lBQ2hFLENBQUM7SUFFRDs7Ozs7Ozs7O09BU0c7SUFDTSxLQUFLLENBQUMsS0FBSyxDQUNsQixTQUF3QixFQUN4QixLQUFxQixFQUNyQixLQUF3QjtRQUV4QixPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxLQUFLLENBQUMsS0FBSyxDQUFDLFNBQVMsRUFBRSxLQUFLLEVBQUUsS0FBSyxDQUFDLEVBQUU7WUFDL0QsSUFBSSxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxJQUFJLEVBQUUsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFO1NBQzFDLENBQWEsQ0FBQztJQUNqQixDQUFDO0lB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QUFVLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIFJhbUZsYWdzLFxuICBSYXdSYW1RdWVyeSxcbiAgUmFtU3RvcmFnZSxcbiAgUmFtUmVwb3NpdG9yeSxcbiAgUmFtQ29uZmlnLFxufSBmcm9tIFwiLi90eXBlc1wiO1xuaW1wb3J0IHsgUmFtU3RhdGVtZW50IH0gZnJvbSBcIi4vUmFtU3RhdGVtZW50XCI7XG5pbXBvcnQgeyBSYW1Db250ZXh0IH0gZnJvbSBcIi4vUmFtQ29udGV4dFwiO1xuaW1wb3J0IHsgUmVwb3NpdG9yeSB9IGZyb20gXCIuLi9yZXBvc2l0b3J5L1JlcG9zaXRvcnlcIjtcbmltcG9ydCB7IEFkYXB0ZXIsIFBlcnNpc3RlbmNlS2V5cywgU2VxdWVuY2UgfSBmcm9tIFwiLi4vcGVyc2lzdGVuY2VcIjtcbmltcG9ydCB7IFNlcXVlbmNlT3B0aW9ucyB9IGZyb20gXCIuLi9pbnRlcmZhY2VzXCI7XG5pbXBvcnQgeyBMb2NrIH0gZnJvbSBcIkBkZWNhZi10cy90cmFuc2FjdGlvbmFsLWRlY29yYXRvcnNcIjtcbmltcG9ydCB7XG4gIENvbnN0cnVjdG9yLFxuICBEZWNvcmF0aW9uLFxuICBoYXNoT2JqLFxuICBNb2RlbCxcbiAgcHJvcE1ldGFkYXRhLFxufSBmcm9tIFwiQGRlY2FmLXRzL2RlY29yYXRvci12YWxpZGF0aW9uXCI7XG5pbXBvcnQge1xuICBCYXNlRXJyb3IsXG4gIENvbmZsaWN0RXJyb3IsXG4gIGZpbmRQcmltYXJ5S2V5LFxuICBJbnRlcm5hbEVycm9yLFxuICBOb3RGb3VuZEVycm9yLFxuICBvbkNyZWF0ZSxcbiAgb25DcmVhdGVVcGRhdGUsXG4gIE9wZXJhdGlvbktleXMsXG59IGZyb20gXCJAZGVjYWYtdHMvZGItZGVjb3JhdG9yc1wiO1xuaW1wb3J0IHsgUmFtU2VxdWVuY2UgfSBmcm9tIFwiLi9SYW1TZXF1ZW5jZVwiO1xuaW1wb3J0IHsgY3JlYXRlZEJ5T25SYW1DcmVhdGVVcGRhdGUgfSBmcm9tIFwiLi9oYW5kbGVyc1wiO1xuaW1wb3J0IHsgUmFtRmxhdm91ciB9IGZyb20gXCIuL2NvbnN0YW50c1wiO1xuXG5BZGFwdGVyLnNldEN1cnJlbnQoUmFtRmxhdm91cik7XG5cbi8qKlxuICogQGRlc2NyaXB0aW9uIEluLW1lbW9yeSBhZGFwdGVyIGZvciBkYXRhIHBlcnNpc3RlbmNlXG4gKiBAc3VtbWFyeSBUaGUgUmFtQWRhcHRlciBwcm92aWRlcyBhbiBpbi1tZW1vcnkgaW1wbGVtZW50YXRpb24gb2YgdGhlIHBlcnNpc3RlbmNlIGxheWVyLlxuICogSXQgc3RvcmVzIGRhdGEgaW4gSmF2YVNjcmlwdCBNYXBzIGFuZCBwcm92aWRlcyBDUlVEIG9wZXJhdGlvbnMgYW5kIHF1ZXJ5IGNhcGFiaWxpdGllcy5cbiAqIFRoaXMgYWRhcHRlciBpcyB1c2VmdWwgZm9yIHRlc3RpbmcsIHByb3RvdHlwaW5nLCBhbmQgYXBwbGljYXRpb25zIHRoYXQgZG9uJ3QgcmVxdWlyZVxuICogcGVyc2lzdGVudCBzdG9yYWdlIGFjcm9zcyBhcHBsaWNhdGlvbiByZXN0YXJ0cy5cbiAqIEBjbGFzcyBSYW1BZGFwdGVyXG4gKiBAY2F0ZWdvcnkgUmFtXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogLy8gQ3JlYXRlIGEgbmV3IFJBTSBhZGFwdGVyXG4gKiBjb25zdCBhZGFwdGVyID0gbmV3IFJhbUFkYXB0ZXIoJ215UmFtQWRhcHRlcicpO1xuICpcbiAqIC8vIENyZWF0ZSBhIHJlcG9zaXRvcnkgZm9yIGEgbW9kZWxcbiAqIGNvbnN0IHVzZXJSZXBvID0gbmV3IChhZGFwdGVyLnJlcG9zaXRvcnk8VXNlcj4oKSkoVXNlciwgYWRhcHRlcik7XG4gKlxuICogLy8gUGVyZm9ybSBDUlVEIG9wZXJhdGlvbnNcbiAqIGNvbnN0IHVzZXIgPSBuZXcgVXNlcih7IG5hbWU6ICdKb2huJywgZW1haWw6ICdqb2huQGV4YW1wbGUuY29tJyB9KTtcbiAqIGF3YWl0IHVzZXJSZXBvLmNyZWF0ZSh1c2VyKTtcbiAqIGNvbnN0IHJldHJpZXZlZFVzZXIgPSBhd2FpdCB1c2VyUmVwby5maW5kQnlJZCh1c2VyLmlkKTtcbiAqIGBgYFxuICogQG1lcm1haWRcbiAqIHNlcXVlbmNlRGlhZ3JhbVxuICogICBwYXJ0aWNpcGFudCBDbGllbnRcbiAqICAgcGFydGljaXBhbnQgUmVwb3NpdG9yeVxuICogICBwYXJ0aWNpcGFudCBSYW1BZGFwdGVyXG4gKiAgIHBhcnRpY2lwYW50IFN0b3JhZ2UgYXMgSW4tTWVtb3J5IFN0b3JhZ2VcbiAqXG4gKiAgIENsaWVudC0+PlJlcG9zaXRvcnk6IGNyZWF0ZShtb2RlbClcbiAqICAgUmVwb3NpdG9yeS0+PlJhbUFkYXB0ZXI6IGNyZWF0ZSh0YWJsZU5hbWUsIGlkLCBtb2RlbClcbiAqICAgUmFtQWRhcHRlci0+PlJhbUFkYXB0ZXI6IGxvY2suYWNxdWlyZSgpXG4gKiAgIFJhbUFkYXB0ZXItPj5TdG9yYWdlOiBzZXQoaWQsIG1vZGVsKVxuICogICBSYW1BZGFwdGVyLT4+UmFtQWRhcHRlcjogbG9jay5yZWxlYXNlKClcbiAqICAgUmFtQWRhcHRlci0tPj5SZXBvc2l0b3J5OiBtb2RlbFxuICogICBSZXBvc2l0b3J5LS0+PkNsaWVudDogbW9kZWxcbiAqXG4gKiAgIENsaWVudC0+PlJlcG9zaXRvcnk6IGZpbmRCeUlkKGlkKVxuICogICBSZXBvc2l0b3J5LT4+UmFtQWRhcHRlcjogcmVhZCh0YWJsZU5hbWUsIGlkKVxuICogICBSYW1BZGFwdGVyLT4+U3RvcmFnZTogZ2V0KGlkKVxuICogICBTdG9yYWdlLS0+PlJhbUFkYXB0ZXI6IG1vZGVsXG4gKiAgIFJhbUFkYXB0ZXItLT4+UmVwb3NpdG9yeTogbW9kZWxcbiAqICAgUmVwb3NpdG9yeS0tPj5DbGllbnQ6IG1vZGVsXG4gKi9cbmV4cG9ydCBjbGFzcyBSYW1BZGFwdGVyIGV4dGVuZHMgQWRhcHRlcjxcbiAgUmFtQ29uZmlnLFxuICBSYW1TdG9yYWdlLFxuICBSYXdSYW1RdWVyeTxhbnk+LFxuICBSYW1GbGFncyxcbiAgUmFtQ29udGV4dFxuPiB7XG4gIGNvbnN0cnVjdG9yKGNvbmY6IFJhbUNvbmZpZyA9IHt9IGFzIGFueSwgYWxpYXM/OiBzdHJpbmcpIHtcbiAgICBzdXBlcihjb25mLCBSYW1GbGF2b3VyLCBhbGlhcyk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEdldHMgdGhlIHJlcG9zaXRvcnkgY29uc3RydWN0b3IgZm9yIGEgbW9kZWxcbiAgICogQHN1bW1hcnkgUmV0dXJucyBhIGNvbnN0cnVjdG9yIGZvciBjcmVhdGluZyByZXBvc2l0b3JpZXMgdGhhdCB3b3JrIHdpdGggdGhlIHNwZWNpZmllZCBtb2RlbCB0eXBlLlxuICAgKiBUaGlzIG1ldGhvZCBvdmVycmlkZXMgdGhlIGJhc2UgaW1wbGVtZW50YXRpb24gdG8gcHJvdmlkZSBSQU0tc3BlY2lmaWMgcmVwb3NpdG9yeSBmdW5jdGlvbmFsaXR5LlxuICAgKiBAdGVtcGxhdGUgTSAtIFRoZSBtb2RlbCB0eXBlIGZvciB0aGUgcmVwb3NpdG9yeVxuICAgKiBAcmV0dXJuIHtDb25zdHJ1Y3RvcjxSYW1SZXBvc2l0b3J5PE0+Pn0gQSBjb25zdHJ1Y3RvciBmb3IgY3JlYXRpbmcgUkFNIHJlcG9zaXRvcmllc1xuICAgKi9cbiAgb3ZlcnJpZGUgcmVwb3NpdG9yeTxNIGV4dGVuZHMgTW9kZWw8Ym9vbGVhbj4+KCk6IENvbnN0cnVjdG9yPFxuICAgIFJhbVJlcG9zaXRvcnk8TT5cbiAgPiB7XG4gICAgcmV0dXJuIHN1cGVyLnJlcG9zaXRvcnk8TT4oKSBhcyBDb25zdHJ1Y3RvcjxSYW1SZXBvc2l0b3J5PE0+PjtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQ3JlYXRlcyBvcGVyYXRpb24gZmxhZ3Mgd2l0aCBVVUlEXG4gICAqIEBzdW1tYXJ5IEV4dGVuZHMgdGhlIGJhc2UgZmxhZ3Mgd2l0aCBhIFVVSUQgZm9yIHVzZXIgaWRlbnRpZmljYXRpb24uXG4gICAqIFRoaXMgbWV0aG9kIGVuc3VyZXMgdGhhdCBhbGwgb3BlcmF0aW9ucyBoYXZlIGEgdW5pcXVlIGlkZW50aWZpZXIgZm9yIHRyYWNraW5nIHB1cnBvc2VzLlxuICAgKiBAdGVtcGxhdGUgTSAtIFRoZSBtb2RlbCB0eXBlIGZvciB0aGUgb3BlcmF0aW9uXG4gICAqIEBwYXJhbSB7T3BlcmF0aW9uS2V5c30gb3BlcmF0aW9uIC0gVGhlIHR5cGUgb2Ygb3BlcmF0aW9uIGJlaW5nIHBlcmZvcm1lZFxuICAgKiBAcGFyYW0ge0NvbnN0cnVjdG9yPE0+fSBtb2RlbCAtIFRoZSBtb2RlbCBjb25zdHJ1Y3RvclxuICAgKiBAcGFyYW0ge1BhcnRpYWw8UmFtRmxhZ3M+fSBmbGFncyAtIFBhcnRpYWwgZmxhZ3MgdG8gYmUgZXh0ZW5kZWRcbiAgICogQHJldHVybiB7UHJvbWlzZTxSYW1GbGFncz59IENvbXBsZXRlIGZsYWdzIHdpdGggVVVJRFxuICAgKi9cbiAgb3ZlcnJpZGUgYXN5bmMgZmxhZ3M8TSBleHRlbmRzIE1vZGVsPGJvb2xlYW4+PihcbiAgICBvcGVyYXRpb246IE9wZXJhdGlvbktleXMsXG4gICAgbW9kZWw6IENvbnN0cnVjdG9yPE0+LFxuICAgIGZsYWdzOiBQYXJ0aWFsPFJhbUZsYWdzPlxuICApOiBQcm9taXNlPFJhbUZsYWdzPiB7XG4gICAgcmV0dXJuIE9iamVjdC5hc3NpZ24oYXdhaXQgc3VwZXIuZmxhZ3Mob3BlcmF0aW9uLCBtb2RlbCwgZmxhZ3MpLCB7XG4gICAgICBVVUlEOiB0aGlzLmNvbmZpZy51c2VyIHx8IFwiXCIgKyBEYXRlLm5vdygpLFxuICAgIH0pIGFzIFJhbUZsYWdzO1xuICB9XG5cbiAgb3ZlcnJpZGUgQ29udGV4dCA9IFJhbUNvbnRleHQ7XG5cbiAgcHJpdmF0ZSBpbmRleGVzOiBSZWNvcmQ8XG4gICAgc3RyaW5nLFxuICAgIFJlY29yZDxzdHJpbmcgfCBudW1iZXIsIFJlY29yZDxzdHJpbmcsIGFueT4+XG4gID4gPSB7fTtcblxuICBwcml2YXRlIGxvY2sgPSBuZXcgTG9jaygpO1xuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gSW5kZXhlcyBtb2RlbHMgaW4gdGhlIFJBTSBhZGFwdGVyXG4gICAqIEBzdW1tYXJ5IEEgbm8tb3AgaW5kZXhpbmcgbWV0aG9kIGZvciB0aGUgUkFNIGFkYXB0ZXIuXG4gICAqIFNpbmNlIFJBTSBhZGFwdGVyIGRvZXNuJ3QgcmVxdWlyZSBleHBsaWNpdCBpbmRleGluZywgdGhpcyBtZXRob2Qgc2ltcGx5IHJlc29sdmVzIGltbWVkaWF0ZWx5LlxuICAgKiBAcGFyYW0gbW9kZWxzIC0gTW9kZWxzIHRvIGJlIGluZGV4ZWQgKHVudXNlZClcbiAgICogQHJldHVybiB7UHJvbWlzZTxhbnk+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIGluZGV4aW5nIGlzIGNvbXBsZXRlXG4gICAqL1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXVudXNlZC12YXJzXG4gIGFzeW5jIGluZGV4KC4uLm1vZGVsczogUmVjb3JkPHN0cmluZywgYW55PltdKTogUHJvbWlzZTxhbnk+IHtcbiAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHVuZGVmaW5lZCk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFByZXBhcmVzIGEgbW9kZWwgZm9yIHN0b3JhZ2VcbiAgICogQHN1bW1hcnkgQ29udmVydHMgYSBtb2RlbCBpbnN0YW5jZSB0byBhIGZvcm1hdCBzdWl0YWJsZSBmb3Igc3RvcmFnZSBpbiB0aGUgUkFNIGFkYXB0ZXIuXG4gICAqIFRoaXMgbWV0aG9kIGV4dHJhY3RzIHRoZSBwcmltYXJ5IGtleSBhbmQgY3JlYXRlcyBhIHJlY29yZCB3aXRob3V0IHRoZSBwcmltYXJ5IGtleSBmaWVsZC5cbiAgICogQHRlbXBsYXRlIE0gLSBUaGUgbW9kZWwgdHlwZSBiZWluZyBwcmVwYXJlZFxuICAgKiBAcGFyYW0ge019IG1vZGVsIC0gVGhlIG1vZGVsIGluc3RhbmNlIHRvIHByZXBhcmVcbiAgICogQHBhcmFtIHBrIC0gVGhlIHByaW1hcnkga2V5IHByb3BlcnR5IG5hbWVcbiAgICogQHJldHVybiBPYmplY3QgY29udGFpbmluZyB0aGUgcmVjb3JkIGFuZCBJRFxuICAgKi9cbiAgb3ZlcnJpZGUgcHJlcGFyZTxNIGV4dGVuZHMgTW9kZWw+KFxuICAgIG1vZGVsOiBNLFxuICAgIHBrOiBrZXlvZiBNXG4gICk6IHsgcmVjb3JkOiBSZWNvcmQ8c3RyaW5nLCBhbnk+OyBpZDogc3RyaW5nIH0ge1xuICAgIGNvbnN0IHByZXBhcmVkID0gc3VwZXIucHJlcGFyZShtb2RlbCwgcGspO1xuICAgIGRlbGV0ZSBwcmVwYXJlZC5yZWNvcmRbcGsgYXMgc3RyaW5nXTtcbiAgICByZXR1cm4gcHJlcGFyZWQ7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIENvbnZlcnRzIGEgc3RvcmVkIHJlY29yZCBiYWNrIHRvIGEgbW9kZWwgaW5zdGFuY2VcbiAgICogQHN1bW1hcnkgUmVjb25zdHJ1Y3RzIGEgbW9kZWwgaW5zdGFuY2UgZnJvbSBhIHN0b3JlZCByZWNvcmQgYnkgYWRkaW5nIGJhY2sgdGhlIHByaW1hcnkga2V5LlxuICAgKiBUaGlzIG1ldGhvZCBpcyB0aGUgaW52ZXJzZSBvZiB0aGUgcHJlcGFyZSBtZXRob2QuXG4gICAqIEB0ZW1wbGF0ZSBNIC0gVGhlIG1vZGVsIHR5cGUgdG8gcmV2ZXJ0IHRvXG4gICAqIEBwYXJhbSB7UmVjb3JkPHN0cmluZywgYW55Pn0gb2JqIC0gVGhlIHN0b3JlZCByZWNvcmRcbiAgICogQHBhcmFtIHtzdHJpbmcgfCBDb25zdHJ1Y3RvcjxNPn0gY2xhenogLSBUaGUgbW9kZWwgY2xhc3Mgb3IgbmFtZVxuICAgKiBAcGFyYW0gcGsgLSBUaGUgcHJpbWFyeSBrZXkgcHJvcGVydHkgbmFtZVxuICAgKiBAcGFyYW0ge3N0cmluZyB8IG51bWJlcn0gaWQgLSBUaGUgcHJpbWFyeSBrZXkgdmFsdWVcbiAgICogQHJldHVybiB7TX0gVGhlIHJlY29uc3RydWN0ZWQgbW9kZWwgaW5zdGFuY2VcbiAgICovXG4gIG92ZXJyaWRlIHJldmVydDxNIGV4dGVuZHMgTW9kZWw+KFxuICAgIG9iajogUmVjb3JkPHN0cmluZywgYW55PixcbiAgICBjbGF6ejogc3RyaW5nIHwgQ29uc3RydWN0b3I8TT4sXG4gICAgcGs6IGtleW9mIE0sXG4gICAgaWQ6IHN0cmluZyB8IG51bWJlclxuICApOiBNIHtcbiAgICBjb25zdCByZXMgPSBzdXBlci5yZXZlcnQob2JqLCBjbGF6eiwgcGssIGlkKTtcbiAgICByZXR1cm4gcmVzO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBDcmVhdGVzIGEgbmV3IHJlY29yZCBpbiB0aGUgaW4tbWVtb3J5IHN0b3JhZ2VcbiAgICogQHN1bW1hcnkgU3RvcmVzIGEgbmV3IHJlY29yZCBpbiB0aGUgc3BlY2lmaWVkIHRhYmxlIHdpdGggdGhlIGdpdmVuIElELlxuICAgKiBUaGlzIG1ldGhvZCBhY3F1aXJlcyBhIGxvY2sgdG8gZW5zdXJlIHRocmVhZCBzYWZldHksIGNyZWF0ZXMgdGhlIHRhYmxlIGlmIGl0IGRvZXNuJ3QgZXhpc3QsXG4gICAqIGNoZWNrcyBmb3IgY29uZmxpY3RzLCBhbmQgc3RvcmVzIHRoZSBtb2RlbC5cbiAgICogQHBhcmFtIHtzdHJpbmd9IHRhYmxlTmFtZSAtIFRoZSBuYW1lIG9mIHRoZSB0YWJsZSB0byBzdG9yZSB0aGUgcmVjb3JkIGluXG4gICAqIEBwYXJhbSB7c3RyaW5nIHwgbnVtYmVyfSBpZCAtIFRoZSB1bmlxdWUgaWRlbnRpZmllciBmb3IgdGhlIHJlY29yZFxuICAgKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsIGFueT59IG1vZGVsIC0gVGhlIHJlY29yZCBkYXRhIHRvIHN0b3JlXG4gICAqIEByZXR1cm4ge1Byb21pc2U8UmVjb3JkPHN0cmluZywgYW55Pj59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBzdG9yZWQgcmVjb3JkXG4gICAqIEBtZXJtYWlkXG4gICAqIHNlcXVlbmNlRGlhZ3JhbVxuICAgKiAgIHBhcnRpY2lwYW50IENhbGxlclxuICAgKiAgIHBhcnRpY2lwYW50IFJhbUFkYXB0ZXJcbiAgICogICBwYXJ0aWNpcGFudCBTdG9yYWdlIGFzIEluLU1lbW9yeSBTdG9yYWdlXG4gICAqXG4gICAqICAgQ2FsbGVyLT4+UmFtQWRhcHRlcjogY3JlYXRlKHRhYmxlTmFtZSwgaWQsIG1vZGVsKVxuICAgKiAgIFJhbUFkYXB0ZXItPj5SYW1BZGFwdGVyOiBsb2NrLmFjcXVpcmUoKVxuICAgKiAgIFJhbUFkYXB0ZXItPj5TdG9yYWdlOiBoYXModGFibGVOYW1lKVxuICAgKiAgIGFsdCBUYWJsZSBkb2Vzbid0IGV4aXN0XG4gICAqICAgICBSYW1BZGFwdGVyLT4+U3RvcmFnZTogc2V0KHRhYmxlTmFtZSwgbmV3IE1hcCgpKVxuICAgKiAgIGVuZFxuICAgKiAgIFJhbUFkYXB0ZXItPj5TdG9yYWdlOiBoYXMoaWQpXG4gICAqICAgYWx0IFJlY29yZCBleGlzdHNcbiAgICogICAgIFJhbUFkYXB0ZXItLT4+Q2FsbGVyOiB0aHJvdyBDb25mbGljdEVycm9yXG4gICAqICAgZW5kXG4gICAqICAgUmFtQWRhcHRlci0+PlN0b3JhZ2U6IHNldChpZCwgbW9kZWwpXG4gICAqICAgUmFtQWRhcHRlci0+PlJhbUFkYXB0ZXI6IGxvY2sucmVsZWFzZSgpXG4gICAqICAgUmFtQWRhcHRlci0tPj5DYWxsZXI6IG1vZGVsXG4gICAqL1xuICBhc3luYyBjcmVhdGUoXG4gICAgdGFibGVOYW1lOiBzdHJpbmcsXG4gICAgaWQ6IHN0cmluZyB8IG51bWJlcixcbiAgICBtb2RlbDogUmVjb3JkPHN0cmluZywgYW55PlxuICApOiBQcm9taXNlPFJlY29yZDxzdHJpbmcsIGFueT4+IHtcbiAgICBhd2FpdCB0aGlzLmxvY2suYWNxdWlyZSgpO1xuICAgIGlmICghdGhpcy5jbGllbnQuaGFzKHRhYmxlTmFtZSkpIHRoaXMuY2xpZW50LnNldCh0YWJsZU5hbWUsIG5ldyBNYXAoKSk7XG4gICAgaWYgKHRoaXMuY2xpZW50LmdldCh0YWJsZU5hbWUpICYmIHRoaXMuY2xpZW50LmdldCh0YWJsZU5hbWUpPy5oYXMoaWQpKVxuICAgICAgdGhyb3cgbmV3IENvbmZsaWN0RXJyb3IoXG4gICAgICAgIGBSZWNvcmQgd2l0aCBpZCAke2lkfSBhbHJlYWR5IGV4aXN0cyBpbiB0YWJsZSAke3RhYmxlTmFtZX1gXG4gICAgICApO1xuICAgIHRoaXMuY2xpZW50LmdldCh0YWJsZU5hbWUpPy5zZXQoaWQsIG1vZGVsKTtcbiAgICB0aGlzLmxvY2sucmVsZWFzZSgpO1xuICAgIHJldHVybiBtb2RlbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gUmV0cmlldmVzIGEgcmVjb3JkIGZyb20gaW4tbWVtb3J5IHN0b3JhZ2VcbiAgICogQHN1bW1hcnkgRmV0Y2hlcyBhIHJlY29yZCB3aXRoIHRoZSBzcGVjaWZpZWQgSUQgZnJvbSB0aGUgZ2l2ZW4gdGFibGUuXG4gICAqIFRoaXMgbWV0aG9kIGNoZWNrcyBpZiB0aGUgdGFibGUgYW5kIHJlY29yZCBleGlzdCBhbmQgdGhyb3dzIGFwcHJvcHJpYXRlIGVycm9ycyBpZiBub3QuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB0YWJsZU5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgdGFibGUgdG8gcmV0cmlldmUgZnJvbVxuICAgKiBAcGFyYW0ge3N0cmluZyB8IG51bWJlcn0gaWQgLSBUaGUgdW5pcXVlIGlkZW50aWZpZXIgb2YgdGhlIHJlY29yZCB0byByZXRyaWV2ZVxuICAgKiBAcmV0dXJuIHtQcm9taXNlPFJlY29yZDxzdHJpbmcsIGFueT4+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgcmV0cmlldmVkIHJlY29yZFxuICAgKiBAbWVybWFpZFxuICAgKiBzZXF1ZW5jZURpYWdyYW1cbiAgICogICBwYXJ0aWNpcGFudCBDYWxsZXJcbiAgICogICBwYXJ0aWNpcGFudCBSYW1BZGFwdGVyXG4gICAqICAgcGFydGljaXBhbnQgU3RvcmFnZSBhcyBJbi1NZW1vcnkgU3RvcmFnZVxuICAgKlxuICAgKiAgIENhbGxlci0+PlJhbUFkYXB0ZXI6IHJlYWQodGFibGVOYW1lLCBpZClcbiAgICogICBSYW1BZGFwdGVyLT4+U3RvcmFnZTogaGFzKHRhYmxlTmFtZSlcbiAgICogICBhbHQgVGFibGUgZG9lc24ndCBleGlzdFxuICAgKiAgICAgUmFtQWRhcHRlci0tPj5DYWxsZXI6IHRocm93IE5vdEZvdW5kRXJyb3JcbiAgICogICBlbmRcbiAgICogICBSYW1BZGFwdGVyLT4+U3RvcmFnZTogaGFzKGlkKVxuICAgKiAgIGFsdCBSZWNvcmQgZG9lc24ndCBleGlzdFxuICAgKiAgICAgUmFtQWRhcHRlci0tPj5DYWxsZXI6IHRocm93IE5vdEZvdW5kRXJyb3JcbiAgICogICBlbmRcbiAgICogICBSYW1BZGFwdGVyLT4+U3RvcmFnZTogZ2V0KGlkKVxuICAgKiAgIFN0b3JhZ2UtLT4+UmFtQWRhcHRlcjogcmVjb3JkXG4gICAqICAgUmFtQWRhcHRlci0tPj5DYWxsZXI6IHJlY29yZFxuICAgKi9cbiAgYXN5bmMgcmVhZChcbiAgICB0YWJsZU5hbWU6IHN0cmluZyxcbiAgICBpZDogc3RyaW5nIHwgbnVtYmVyXG4gICk6IFByb21pc2U8UmVjb3JkPHN0cmluZywgYW55Pj4ge1xuICAgIGlmICghdGhpcy5jbGllbnQuaGFzKHRhYmxlTmFtZSkpXG4gICAgICB0aHJvdyBuZXcgTm90Rm91bmRFcnJvcihgVGFibGUgJHt0YWJsZU5hbWV9IG5vdCBmb3VuZGApO1xuICAgIGlmICghdGhpcy5jbGllbnQuZ2V0KHRhYmxlTmFtZSk/LmhhcyhpZCkpXG4gICAgICB0aHJvdyBuZXcgTm90Rm91bmRFcnJvcihcbiAgICAgICAgYFJlY29yZCB3aXRoIGlkICR7aWR9IG5vdCBmb3VuZCBpbiB0YWJsZSAke3RhYmxlTmFtZX1gXG4gICAgICApO1xuICAgIHJldHVybiB0aGlzLmNsaWVudC5nZXQodGFibGVOYW1lKT8uZ2V0KGlkKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gVXBkYXRlcyBhbiBleGlzdGluZyByZWNvcmQgaW4gdGhlIGluLW1lbW9yeSBzdG9yYWdlXG4gICAqIEBzdW1tYXJ5IFVwZGF0ZXMgYSByZWNvcmQgd2l0aCB0aGUgc3BlY2lmaWVkIElEIGluIHRoZSBnaXZlbiB0YWJsZS5cbiAgICogVGhpcyBtZXRob2QgYWNxdWlyZXMgYSBsb2NrIHRvIGVuc3VyZSB0aHJlYWQgc2FmZXR5LCBjaGVja3MgaWYgdGhlIHRhYmxlIGFuZCByZWNvcmQgZXhpc3QsXG4gICAqIGFuZCB1cGRhdGVzIHRoZSByZWNvcmQgd2l0aCB0aGUgbmV3IGRhdGEuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB0YWJsZU5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgdGFibGUgY29udGFpbmluZyB0aGUgcmVjb3JkXG4gICAqIEBwYXJhbSB7c3RyaW5nIHwgbnVtYmVyfSBpZCAtIFRoZSB1bmlxdWUgaWRlbnRpZmllciBvZiB0aGUgcmVjb3JkIHRvIHVwZGF0ZVxuICAgKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsIGFueT59IG1vZGVsIC0gVGhlIG5ldyByZWNvcmQgZGF0YVxuICAgKiBAcmV0dXJuIHtQcm9taXNlPFJlY29yZDxzdHJpbmcsIGFueT4+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgdXBkYXRlZCByZWNvcmRcbiAgICogQG1lcm1haWRcbiAgICogc2VxdWVuY2VEaWFncmFtXG4gICAqICAgcGFydGljaXBhbnQgQ2FsbGVyXG4gICAqICAgcGFydGljaXBhbnQgUmFtQWRhcHRlclxuICAgKiAgIHBhcnRpY2lwYW50IFN0b3JhZ2UgYXMgSW4tTWVtb3J5IFN0b3JhZ2VcbiAgICpcbiAgICogICBDYWxsZXItPj5SYW1BZGFwdGVyOiB1cGRhdGUodGFibGVOYW1lLCBpZCwgbW9kZWwpXG4gICAqICAgUmFtQWRhcHRlci0+PlJhbUFkYXB0ZXI6IGxvY2suYWNxdWlyZSgpXG4gICAqICAgUmFtQWRhcHRlci0+PlN0b3JhZ2U6IGhhcyh0YWJsZU5hbWUpXG4gICAqICAgYWx0IFRhYmxlIGRvZXNuJ3QgZXhpc3RcbiAgICogICAgIFJhbUFkYXB0ZXItLT4+Q2FsbGVyOiB0aHJvdyBOb3RGb3VuZEVycm9yXG4gICAqICAgZW5kXG4gICAqICAgUmFtQWRhcHRlci0+PlN0b3JhZ2U6IGhhcyhpZClcbiAgICogICBhbHQgUmVjb3JkIGRvZXNuJ3QgZXhpc3RcbiAgICogICAgIFJhbUFkYXB0ZXItLT4+Q2FsbGVyOiB0aHJvdyBOb3RGb3VuZEVycm9yXG4gICAqICAgZW5kXG4gICAqICAgUmFtQWRhcHRlci0+PlN0b3JhZ2U6IHNldChpZCwgbW9kZWwpXG4gICAqICAgUmFtQWRhcHRlci0+PlJhbUFkYXB0ZXI6IGxvY2sucmVsZWFzZSgpXG4gICAqICAgUmFtQWRhcHRlci0tPj5DYWxsZXI6IG1vZGVsXG4gICAqL1xuICBhc3luYyB1cGRhdGUoXG4gICAgdGFibGVOYW1lOiBzdHJpbmcsXG4gICAgaWQ6IHN0cmluZyB8IG51bWJlcixcbiAgICBtb2RlbDogUmVjb3JkPHN0cmluZywgYW55PlxuICApOiBQcm9taXNlPFJlY29yZDxzdHJpbmcsIGFueT4+IHtcbiAgICBhd2FpdCB0aGlzLmxvY2suYWNxdWlyZSgpO1xuICAgIGlmICghdGhpcy5jbGllbnQuaGFzKHRhYmxlTmFtZSkpXG4gICAgICB0aHJvdyBuZXcgTm90Rm91bmRFcnJvcihgVGFibGUgJHt0YWJsZU5hbWV9IG5vdCBmb3VuZGApO1xuICAgIGlmICghdGhpcy5jbGllbnQuZ2V0KHRhYmxlTmFtZSk/LmhhcyhpZCkpXG4gICAgICB0aHJvdyBuZXcgTm90Rm91bmRFcnJvcihcbiAgICAgICAgYFJlY29yZCB3aXRoIGlkICR7aWR9IG5vdCBmb3VuZCBpbiB0YWJsZSAke3RhYmxlTmFtZX1gXG4gICAgICApO1xuICAgIHRoaXMuY2xpZW50LmdldCh0YWJsZU5hbWUpPy5zZXQoaWQsIG1vZGVsKTtcbiAgICB0aGlzLmxvY2sucmVsZWFzZSgpO1xuICAgIHJldHVybiBtb2RlbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gRGVsZXRlcyBhIHJlY29yZCBmcm9tIHRoZSBpbi1tZW1vcnkgc3RvcmFnZVxuICAgKiBAc3VtbWFyeSBSZW1vdmVzIGEgcmVjb3JkIHdpdGggdGhlIHNwZWNpZmllZCBJRCBmcm9tIHRoZSBnaXZlbiB0YWJsZS5cbiAgICogVGhpcyBtZXRob2QgYWNxdWlyZXMgYSBsb2NrIHRvIGVuc3VyZSB0aHJlYWQgc2FmZXR5LCBjaGVja3MgaWYgdGhlIHRhYmxlIGFuZCByZWNvcmQgZXhpc3QsXG4gICAqIHJldHJpZXZlcyB0aGUgcmVjb3JkIGJlZm9yZSBkZWxldGlvbiwgYW5kIHRoZW4gcmVtb3ZlcyBpdCBmcm9tIHN0b3JhZ2UuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB0YWJsZU5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgdGFibGUgY29udGFpbmluZyB0aGUgcmVjb3JkXG4gICAqIEBwYXJhbSB7c3RyaW5nIHwgbnVtYmVyfSBpZCAtIFRoZSB1bmlxdWUgaWRlbnRpZmllciBvZiB0aGUgcmVjb3JkIHRvIGRlbGV0ZVxuICAgKiBAcmV0dXJuIHtQcm9taXNlPFJlY29yZDxzdHJpbmcsIGFueT4+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgZGVsZXRlZCByZWNvcmRcbiAgICogQG1lcm1haWRcbiAgICogc2VxdWVuY2VEaWFncmFtXG4gICAqICAgcGFydGljaXBhbnQgQ2FsbGVyXG4gICAqICAgcGFydGljaXBhbnQgUmFtQWRhcHRlclxuICAgKiAgIHBhcnRpY2lwYW50IFN0b3JhZ2UgYXMgSW4tTWVtb3J5IFN0b3JhZ2VcbiAgICpcbiAgICogICBDYWxsZXItPj5SYW1BZGFwdGVyOiBkZWxldGUodGFibGVOYW1lLCBpZClcbiAgICogICBSYW1BZGFwdGVyLT4+UmFtQWRhcHRlcjogbG9jay5hY3F1aXJlKClcbiAgICogICBSYW1BZGFwdGVyLT4+U3RvcmFnZTogaGFzKHRhYmxlTmFtZSlcbiAgICogICBhbHQgVGFibGUgZG9lc24ndCBleGlzdFxuICAgKiAgICAgUmFtQWRhcHRlci0tPj5DYWxsZXI6IHRocm93IE5vdEZvdW5kRXJyb3JcbiAgICogICBlbmRcbiAgICogICBSYW1BZGFwdGVyLT4+U3RvcmFnZTogaGFzKGlkKVxuICAgKiAgIGFsdCBSZWNvcmQgZG9lc24ndCBleGlzdFxuICAgKiAgICAgUmFtQWRhcHRlci0tPj5DYWxsZXI6IHRocm93IE5vdEZvdW5kRXJyb3JcbiAgICogICBlbmRcbiAgICogICBSYW1BZGFwdGVyLT4+U3RvcmFnZTogZ2V0KGlkKVxuICAgKiAgIFN0b3JhZ2UtLT4+UmFtQWRhcHRlcjogcmVjb3JkXG4gICAqICAgUmFtQWRhcHRlci0+PlN0b3JhZ2U6IGRlbGV0ZShpZClcbiAgICogICBSYW1BZGFwdGVyLT4+UmFtQWRhcHRlcjogbG9jay5yZWxlYXNlKClcbiAgICogICBSYW1BZGFwdGVyLS0+PkNhbGxlcjogcmVjb3JkXG4gICAqL1xuICBhc3luYyBkZWxldGUoXG4gICAgdGFibGVOYW1lOiBzdHJpbmcsXG4gICAgaWQ6IHN0cmluZyB8IG51bWJlclxuICApOiBQcm9taXNlPFJlY29yZDxzdHJpbmcsIGFueT4+IHtcbiAgICBhd2FpdCB0aGlzLmxvY2suYWNxdWlyZSgpO1xuICAgIGlmICghdGhpcy5jbGllbnQuaGFzKHRhYmxlTmFtZSkpXG4gICAgICB0aHJvdyBuZXcgTm90Rm91bmRFcnJvcihgVGFibGUgJHt0YWJsZU5hbWV9IG5vdCBmb3VuZGApO1xuICAgIGlmICghdGhpcy5jbGllbnQuZ2V0KHRhYmxlTmFtZSk/LmhhcyhpZCkpXG4gICAgICB0aHJvdyBuZXcgTm90Rm91bmRFcnJvcihcbiAgICAgICAgYFJlY29yZCB3aXRoIGlkICR7aWR9IG5vdCBmb3VuZCBpbiB0YWJsZSAke3RhYmxlTmFtZX1gXG4gICAgICApO1xuICAgIGNvbnN0IG5hdGl2ZWQgPSB0aGlzLmNsaWVudC5nZXQodGFibGVOYW1lKT8uZ2V0KGlkKTtcbiAgICB0aGlzLmNsaWVudC5nZXQodGFibGVOYW1lKT8uZGVsZXRlKGlkKTtcbiAgICB0aGlzLmxvY2sucmVsZWFzZSgpO1xuICAgIHJldHVybiBuYXRpdmVkO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBHZXRzIG9yIGNyZWF0ZXMgYSB0YWJsZSBpbiB0aGUgaW4tbWVtb3J5IHN0b3JhZ2VcbiAgICogQHN1bW1hcnkgUmV0cmlldmVzIHRoZSBNYXAgcmVwcmVzZW50aW5nIGEgdGFibGUgZm9yIGEgZ2l2ZW4gbW9kZWwgb3IgdGFibGUgbmFtZS5cbiAgICogSWYgdGhlIHRhYmxlIGRvZXNuJ3QgZXhpc3QsIGl0IGNyZWF0ZXMgYSBuZXcgb25lLiBUaGlzIGlzIGEgaGVscGVyIG1ldGhvZCB1c2VkXG4gICAqIGJ5IG90aGVyIG1ldGhvZHMgdG8gYWNjZXNzIHRoZSBjb3JyZWN0IHN0b3JhZ2UgbG9jYXRpb24uXG4gICAqIEB0ZW1wbGF0ZSBNIC0gVGhlIG1vZGVsIHR5cGUgZm9yIHRoZSB0YWJsZVxuICAgKiBAcGFyYW0ge3N0cmluZyB8IENvbnN0cnVjdG9yPE0+fSBmcm9tIC0gVGhlIG1vZGVsIGNsYXNzIG9yIHRhYmxlIG5hbWVcbiAgICogQHJldHVybiB7TWFwPHN0cmluZyB8IG51bWJlciwgYW55PiB8IHVuZGVmaW5lZH0gVGhlIHRhYmxlIE1hcCBvciB1bmRlZmluZWRcbiAgICovXG4gIHByb3RlY3RlZCB0YWJsZUZvcjxNIGV4dGVuZHMgTW9kZWw+KGZyb206IHN0cmluZyB8IENvbnN0cnVjdG9yPE0+KSB7XG4gICAgaWYgKHR5cGVvZiBmcm9tID09PSBcInN0cmluZ1wiKSBmcm9tID0gTW9kZWwuZ2V0KGZyb20pIGFzIENvbnN0cnVjdG9yPE0+O1xuICAgIGNvbnN0IHRhYmxlID0gUmVwb3NpdG9yeS50YWJsZShmcm9tKTtcbiAgICBpZiAoIXRoaXMuY2xpZW50Lmhhcyh0YWJsZSkpIHRoaXMuY2xpZW50LnNldCh0YWJsZSwgbmV3IE1hcCgpKTtcbiAgICByZXR1cm4gdGhpcy5jbGllbnQuZ2V0KHRhYmxlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gRXhlY3V0ZXMgYSByYXcgcXVlcnkgYWdhaW5zdCB0aGUgaW4tbWVtb3J5IHN0b3JhZ2VcbiAgICogQHN1bW1hcnkgUGVyZm9ybXMgYSBxdWVyeSBvcGVyYXRpb24gb24gdGhlIGluLW1lbW9yeSBkYXRhIHN0b3JlIHVzaW5nIHRoZSBwcm92aWRlZCBxdWVyeSBzcGVjaWZpY2F0aW9uLlxuICAgKiBUaGlzIG1ldGhvZCBzdXBwb3J0cyBmaWx0ZXJpbmcsIHNvcnRpbmcsIHBhZ2luYXRpb24sIGFuZCBmaWVsZCBzZWxlY3Rpb24uXG4gICAqIEB0ZW1wbGF0ZSBSIC0gVGhlIHJldHVybiB0eXBlIG9mIHRoZSBxdWVyeVxuICAgKiBAcGFyYW0ge1Jhd1JhbVF1ZXJ5PGFueT59IHJhd0lucHV0IC0gVGhlIHF1ZXJ5IHNwZWNpZmljYXRpb25cbiAgICogQHJldHVybiB7UHJvbWlzZTxSPn0gQSBwcm9taXNlIHRoYXQgcm