UNPKG

@decaf-ts/for-nano

Version:

decaf-ts persistence adapter for CouchDB via nano

210 lines 25.8 kB
import { Dispatch } from "@decaf-ts/core"; import { InternalError, OperationKeys } from "@decaf-ts/db-decorators"; import { CouchDBKeys } from "@decaf-ts/for-couchdb"; /** * @description Dispatcher for Nano database change events * @summary Handles the subscription to and processing of database change events from a Nano database, * notifying observers when documents are created, updated, or deleted * @template DocumentScope - The Nano document scope type * @param {number} [timeout=5000] - Timeout in milliseconds for change feed requests * @class NanoDispatch * @example * ```typescript * // Create a dispatcher for a Nano database * const db = server.db.use('my_database'); * const adapter = new NanoAdapter(db); * const dispatch = new NanoDispatch(); * * // The dispatcher will automatically subscribe to changes * // and notify observers when documents change * ``` * @mermaid * classDiagram * class Dispatch { * +initialize() * +updateObservers() * } * class NanoDispatch { * -observerLastUpdate?: string * -attemptCounter: number * -timeout: number * +constructor(timeout) * #changeHandler() * #initialize() * } * Dispatch <|-- NanoDispatch */ export class NanoDispatch extends Dispatch { constructor(timeout = 5000) { super(); this.timeout = timeout; this.attemptCounter = 0; } /** * @description Processes database change events * @summary Handles the response from the Nano changes feed, processes the changes, * and notifies observers about document changes * @param {RequestError | null} error - Error object if the request failed * @param response - The changes response from Nano * @param {any} [headers] - Response headers (unused) * @return {Promise<void>} A promise that resolves when all changes have been processed * @mermaid * sequenceDiagram * participant D as NanoDispatch * participant L as Logger * participant O as Observers * Note over D: Receive changes from Nano * alt Error in response * D->>L: Log error * D-->>D: Return early * end * alt Response is string * D->>D: Parse JSON from string * end * D->>D: Process changes * D->>D: Group changes by table and operation * loop For each table * loop For each operation * D->>O: updateObservers(table, operation, ids) * D->>D: Update observerLastUpdate * D->>L: Log successful dispatch * end * end */ async changeHandler(error, response, // eslint-disable-next-line @typescript-eslint/no-unused-vars headers) { const log = this.log.for(this.changeHandler); if (error) return log.error(`Error in change request: ${error}`); try { response = (typeof response === "string" ? response .split("\n") .filter((r) => !!r) .map((r) => JSON.parse(r)) : response); } catch (e) { return log.error(`Error parsing couchdb change feed: ${e}`); } const count = response.length; if (count > 0) { log.debug(`Received ${count} changes. processing...`); const changes = response .map((rec, i) => { if (i === count - 1) { if (this.observerLastUpdate === rec.last_seq) log.error(`Invalid last update check: ${this.observerLastUpdate} !== ${rec.last_seq}`); return; } const r = rec; const [table, id] = r.id.split(CouchDBKeys.SEPARATOR); return { table: table, id: id, operation: r.deleted ? OperationKeys.DELETE : r.changes[r.changes.length - 1].rev.split("-")[0] === "1" ? OperationKeys.CREATE : OperationKeys.UPDATE, step: r.changes[r.changes.length - 1].rev, }; }) .reduce((accum, r) => { if (!r) return accum; const { table, id, operation, step } = r; if (!accum[table]) accum[table] = {}; if (!accum[table][operation]) accum[table][operation] = { ids: new Set(), step: step }; accum[table][operation].ids.add(id); accum[table][operation].step = step; return accum; }, {}); for (const table of Object.keys(changes)) { for (const op of Object.keys(changes[table])) { try { await this.updateObservers(table, op, [ ...changes[table][op].ids.values(), ]); this.observerLastUpdate = changes[table][op].step; log.verbose(`Observer refresh dispatched by ${op} for ${table}`); log.debug(`pks: ${Array.from(changes[table][op].ids.values())}`); } catch (e) { log.error(`Failed to dispatch observer refresh for ${table}, op ${op}: ${e}`); } } } } } /** * @description Initializes the dispatcher and subscribes to database changes * @summary Sets up the continuous changes feed subscription to the Nano database * and handles reconnection attempts if the connection fails * @return {Promise<void>} A promise that resolves when the subscription is established * @mermaid * sequenceDiagram * participant D as NanoDispatch * participant S as subscribeToCouch * participant DB as Nano Database * participant L as Logger * D->>S: Call subscribeToCouch * S->>S: Check adapter and native * alt No adapter or native * S-->>S: throw InternalError * end * S->>DB: changes(options, changeHandler) * alt Success * DB-->>S: Subscription established * S-->>D: Promise resolves * D->>L: Log successful subscription * else Error * DB-->>S: Error * S->>S: Increment attemptCounter * alt attemptCounter > 3 * S->>L: Log error * S-->>D: Promise rejects * else attemptCounter <= 3 * S->>L: Log retry * S->>S: Wait timeout * S->>S: Recursive call to subscribeToCouch * end * end */ async initialize() { const log = this.log.for(this.initialize); const subLog = log.for(subscribeToCouch); async function subscribeToCouch() { if (!this.adapter || !this.native) throw new InternalError(`No adapter/native observed for dispatch`); try { this.native.changes({ feed: "continuous", include_docs: false, since: this.observerLastUpdate || "now", timeout: this.timeout, }, this.changeHandler.bind(this)); } catch (e) { if (++this.attemptCounter > 3) return subLog.error(`Failed to subscribe to couchdb changes: ${e}`); subLog.info(`Failed to subscribe to couchdb changes: ${e}. Retrying in 5 seconds...`); await new Promise((resolve) => setTimeout(resolve, this.timeout)); return subscribeToCouch.call(this); } } subscribeToCouch .call(this) .then(() => { this.log.info(`Subscribed to couchdb changes`); }) .catch((e) => { throw new InternalError(`Failed to subscribe to couchdb changes: ${e}`); }); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTmFub0Rpc3BhdGNoLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL05hbm9EaXNwYXRjaC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFPMUMsT0FBTyxFQUFFLGFBQWEsRUFBRSxhQUFhLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN2RSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFFcEQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBZ0NHO0FBQ0gsTUFBTSxPQUFPLFlBQWEsU0FBUSxRQUE0QjtJQUc1RCxZQUFvQixVQUFVLElBQUk7UUFDaEMsS0FBSyxFQUFFLENBQUM7UUFEVSxZQUFPLEdBQVAsT0FBTyxDQUFPO1FBRDFCLG1CQUFjLEdBQVcsQ0FBQyxDQUFDO0lBR25DLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O09BOEJHO0lBQ08sS0FBSyxDQUFDLGFBQWEsQ0FDM0IsS0FBMEIsRUFDMUIsUUFBMEU7SUFDMUUsNkRBQTZEO0lBQzdELE9BQWE7UUFFYixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDN0MsSUFBSSxLQUFLO1lBQUUsT0FBTyxHQUFHLENBQUMsS0FBSyxDQUFDLDRCQUE0QixLQUFLLEVBQUUsQ0FBQyxDQUFDO1FBQ2pFLElBQUksQ0FBQztZQUNILFFBQVEsR0FBRyxDQUNULE9BQU8sUUFBUSxLQUFLLFFBQVE7Z0JBQzFCLENBQUMsQ0FBQyxRQUFRO3FCQUNMLEtBQUssQ0FBQyxJQUFJLENBQUM7cUJBQ1gsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO3FCQUNsQixHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLElB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Q0FBQyxFQUFFLENBQUMsQ0FBQztRQUMxRSxDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IERpc3BhdGNoIH0gZnJvbSBcIkBkZWNhZi10cy9jb3JlXCI7XG5pbXBvcnQge1xuICBEYXRhYmFzZUNoYW5nZXNSZXNwb25zZSxcbiAgRGF0YWJhc2VDaGFuZ2VzUmVzdWx0SXRlbSxcbiAgRG9jdW1lbnRTY29wZSxcbiAgUmVxdWVzdEVycm9yLFxufSBmcm9tIFwibmFub1wiO1xuaW1wb3J0IHsgSW50ZXJuYWxFcnJvciwgT3BlcmF0aW9uS2V5cyB9IGZyb20gXCJAZGVjYWYtdHMvZGItZGVjb3JhdG9yc1wiO1xuaW1wb3J0IHsgQ291Y2hEQktleXMgfSBmcm9tIFwiQGRlY2FmLXRzL2Zvci1jb3VjaGRiXCI7XG5cbi8qKlxuICogQGRlc2NyaXB0aW9uIERpc3BhdGNoZXIgZm9yIE5hbm8gZGF0YWJhc2UgY2hhbmdlIGV2ZW50c1xuICogQHN1bW1hcnkgSGFuZGxlcyB0aGUgc3Vic2NyaXB0aW9uIHRvIGFuZCBwcm9jZXNzaW5nIG9mIGRhdGFiYXNlIGNoYW5nZSBldmVudHMgZnJvbSBhIE5hbm8gZGF0YWJhc2UsXG4gKiBub3RpZnlpbmcgb2JzZXJ2ZXJzIHdoZW4gZG9jdW1lbnRzIGFyZSBjcmVhdGVkLCB1cGRhdGVkLCBvciBkZWxldGVkXG4gKiBAdGVtcGxhdGUgRG9jdW1lbnRTY29wZSAtIFRoZSBOYW5vIGRvY3VtZW50IHNjb3BlIHR5cGVcbiAqIEBwYXJhbSB7bnVtYmVyfSBbdGltZW91dD01MDAwXSAtIFRpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBjaGFuZ2UgZmVlZCByZXF1ZXN0c1xuICogQGNsYXNzIE5hbm9EaXNwYXRjaFxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIC8vIENyZWF0ZSBhIGRpc3BhdGNoZXIgZm9yIGEgTmFubyBkYXRhYmFzZVxuICogY29uc3QgZGIgPSBzZXJ2ZXIuZGIudXNlKCdteV9kYXRhYmFzZScpO1xuICogY29uc3QgYWRhcHRlciA9IG5ldyBOYW5vQWRhcHRlcihkYik7XG4gKiBjb25zdCBkaXNwYXRjaCA9IG5ldyBOYW5vRGlzcGF0Y2goKTtcbiAqXG4gKiAvLyBUaGUgZGlzcGF0Y2hlciB3aWxsIGF1dG9tYXRpY2FsbHkgc3Vic2NyaWJlIHRvIGNoYW5nZXNcbiAqIC8vIGFuZCBub3RpZnkgb2JzZXJ2ZXJzIHdoZW4gZG9jdW1lbnRzIGNoYW5nZVxuICogYGBgXG4gKiBAbWVybWFpZFxuICogY2xhc3NEaWFncmFtXG4gKiAgIGNsYXNzIERpc3BhdGNoIHtcbiAqICAgICAraW5pdGlhbGl6ZSgpXG4gKiAgICAgK3VwZGF0ZU9ic2VydmVycygpXG4gKiAgIH1cbiAqICAgY2xhc3MgTmFub0Rpc3BhdGNoIHtcbiAqICAgICAtb2JzZXJ2ZXJMYXN0VXBkYXRlPzogc3RyaW5nXG4gKiAgICAgLWF0dGVtcHRDb3VudGVyOiBudW1iZXJcbiAqICAgICAtdGltZW91dDogbnVtYmVyXG4gKiAgICAgK2NvbnN0cnVjdG9yKHRpbWVvdXQpXG4gKiAgICAgI2NoYW5nZUhhbmRsZXIoKVxuICogICAgICNpbml0aWFsaXplKClcbiAqICAgfVxuICogICBEaXNwYXRjaCA8fC0tIE5hbm9EaXNwYXRjaFxuICovXG5leHBvcnQgY2xhc3MgTmFub0Rpc3BhdGNoIGV4dGVuZHMgRGlzcGF0Y2g8RG9jdW1lbnRTY29wZTxhbnk+PiB7XG4gIHByaXZhdGUgb2JzZXJ2ZXJMYXN0VXBkYXRlPzogc3RyaW5nO1xuICBwcml2YXRlIGF0dGVtcHRDb3VudGVyOiBudW1iZXIgPSAwO1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHRpbWVvdXQgPSA1MDAwKSB7XG4gICAgc3VwZXIoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gUHJvY2Vzc2VzIGRhdGFiYXNlIGNoYW5nZSBldmVudHNcbiAgICogQHN1bW1hcnkgSGFuZGxlcyB0aGUgcmVzcG9uc2UgZnJvbSB0aGUgTmFubyBjaGFuZ2VzIGZlZWQsIHByb2Nlc3NlcyB0aGUgY2hhbmdlcyxcbiAgICogYW5kIG5vdGlmaWVzIG9ic2VydmVycyBhYm91dCBkb2N1bWVudCBjaGFuZ2VzXG4gICAqIEBwYXJhbSB7UmVxdWVzdEVycm9yIHwgbnVsbH0gZXJyb3IgLSBFcnJvciBvYmplY3QgaWYgdGhlIHJlcXVlc3QgZmFpbGVkXG4gICAqIEBwYXJhbSByZXNwb25zZSAtIFRoZSBjaGFuZ2VzIHJlc3BvbnNlIGZyb20gTmFub1xuICAgKiBAcGFyYW0ge2FueX0gW2hlYWRlcnNdIC0gUmVzcG9uc2UgaGVhZGVycyAodW51c2VkKVxuICAgKiBAcmV0dXJuIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIGFsbCBjaGFuZ2VzIGhhdmUgYmVlbiBwcm9jZXNzZWRcbiAgICogQG1lcm1haWRcbiAgICogc2VxdWVuY2VEaWFncmFtXG4gICAqICAgcGFydGljaXBhbnQgRCBhcyBOYW5vRGlzcGF0Y2hcbiAgICogICBwYXJ0aWNpcGFudCBMIGFzIExvZ2dlclxuICAgKiAgIHBhcnRpY2lwYW50IE8gYXMgT2JzZXJ2ZXJzXG4gICAqICAgTm90ZSBvdmVyIEQ6IFJlY2VpdmUgY2hhbmdlcyBmcm9tIE5hbm9cbiAgICogICBhbHQgRXJyb3IgaW4gcmVzcG9uc2VcbiAgICogICAgIEQtPj5MOiBMb2cgZXJyb3JcbiAgICogICAgIEQtLT4+RDogUmV0dXJuIGVhcmx5XG4gICAqICAgZW5kXG4gICAqICAgYWx0IFJlc3BvbnNlIGlzIHN0cmluZ1xuICAgKiAgICAgRC0+PkQ6IFBhcnNlIEpTT04gZnJvbSBzdHJpbmdcbiAgICogICBlbmRcbiAgICogICBELT4+RDogUHJvY2VzcyBjaGFuZ2VzXG4gICAqICAgRC0+PkQ6IEdyb3VwIGNoYW5nZXMgYnkgdGFibGUgYW5kIG9wZXJhdGlvblxuICAgKiAgIGxvb3AgRm9yIGVhY2ggdGFibGVcbiAgICogICAgIGxvb3AgRm9yIGVhY2ggb3BlcmF0aW9uXG4gICAqICAgICAgIEQtPj5POiB1cGRhdGVPYnNlcnZlcnModGFibGUsIG9wZXJhdGlvbiwgaWRzKVxuICAgKiAgICAgICBELT4+RDogVXBkYXRlIG9ic2VydmVyTGFzdFVwZGF0ZVxuICAgKiAgICAgICBELT4+TDogTG9nIHN1Y2Nlc3NmdWwgZGlzcGF0Y2hcbiAgICogICAgIGVuZFxuICAgKiAgIGVuZFxuICAgKi9cbiAgcHJvdGVjdGVkIGFzeW5jIGNoYW5nZUhhbmRsZXIoXG4gICAgZXJyb3I6IFJlcXVlc3RFcnJvciB8IG51bGwsXG4gICAgcmVzcG9uc2U6IChEYXRhYmFzZUNoYW5nZXNSZXNwb25zZSB8IERhdGFiYXNlQ2hhbmdlc1Jlc3VsdEl0ZW0pW10gfCBzdHJpbmcsXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby11bnVzZWQtdmFyc1xuICAgIGhlYWRlcnM/OiBhbnlcbiAgKSB7XG4gICAgY29uc3QgbG9nID0gdGhpcy5sb2cuZm9yKHRoaXMuY2hhbmdlSGFuZGxlcik7XG4gICAgaWYgKGVycm9yKSByZXR1cm4gbG9nLmVycm9yKGBFcnJvciBpbiBjaGFuZ2UgcmVxdWVzdDogJHtlcnJvcn1gKTtcbiAgICB0cnkge1xuICAgICAgcmVzcG9uc2UgPSAoXG4gICAgICAgIHR5cGVvZiByZXNwb25zZSA9PT0gXCJzdHJpbmdcIlxuICAgICAgICAgID8gcmVzcG9uc2VcbiAgICAgICAgICAgICAgLnNwbGl0KFwiXFxuXCIpXG4gICAgICAgICAgICAgIC5maWx0ZXIoKHIpID0+ICEhcilcbiAgICAgICAgICAgICAgLm1hcCgocikgPT4gSlNPTi5wYXJzZShyKSlcbiAgICAgICAgICA6IHJlc3BvbnNlXG4gICAgICApIGFzIERhdGFiYXNlQ2hhbmdlc1Jlc3BvbnNlW107XG4gICAgfSBjYXRjaCAoZTogdW5rbm93bikge1xuICAgICAgcmV0dXJuIGxvZy5lcnJvcihgRXJyb3IgcGFyc2luZyBjb3VjaGRiIGNoYW5nZSBmZWVkOiAke2V9YCk7XG4gICAgfVxuICAgIGNvbnN0IGNvdW50ID0gcmVzcG9uc2UubGVuZ3RoO1xuICAgIGlmIChjb3VudCA+IDApIHtcbiAgICAgIGxvZy5kZWJ1ZyhgUmVjZWl2ZWQgJHtjb3VudH0gY2hhbmdlcy4gcHJvY2Vzc2luZy4uLmApO1xuICAgICAgY29uc3QgY2hhbmdlcyA9IHJlc3BvbnNlXG4gICAgICAgIC5tYXAoKHJlYywgaSkgPT4ge1xuICAgICAgICAgIGlmIChpID09PSBjb3VudCAtIDEpIHtcbiAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgdGhpcy5vYnNlcnZlckxhc3RVcGRhdGUgPT09XG4gICAgICAgICAgICAgIChyZWMgYXMgRGF0YWJhc2VDaGFuZ2VzUmVzcG9uc2UpLmxhc3Rfc2VxXG4gICAgICAgICAgICApXG4gICAgICAgICAgICAgIGxvZy5lcnJvcihcbiAgICAgICAgICAgICAgICBgSW52YWxpZCBsYXN0IHVwZGF0ZSBjaGVjazogJHt0aGlzLm9ic2VydmVyTGFzdFVwZGF0ZX0gIT09ICR7KHJlYyBhcyBEYXRhYmFzZUNoYW5nZXNSZXNwb25zZSkubGFzdF9zZXF9YFxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCByID0gcmVjIGFzIERhdGFiYXNlQ2hhbmdlc1Jlc3VsdEl0ZW07XG4gICAgICAgICAgY29uc3QgW3RhYmxlLCBpZF0gPSByLmlkLnNwbGl0KENvdWNoREJLZXlzLlNFUEFSQVRPUik7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHRhYmxlOiB0YWJsZSxcbiAgICAgICAgICAgIGlkOiBpZCxcbiAgICAgICAgICAgIG9wZXJhdGlvbjogci5kZWxldGVkXG4gICAgICAgICAgICAgID8gT3BlcmF0aW9uS2V5cy5ERUxFVEVcbiAgICAgICAgICAgICAgOiByLmNoYW5nZXNbci5jaGFuZ2VzLmxlbmd0aCAtIDFdLnJldi5zcGxpdChcIi1cIilbMF0gPT09IFwiMVwiXG4gICAgICAgICAgICAgICAgPyBPcGVyYXRpb25LZXlzLkNSRUFURVxuICAgICAgICAgICAgICAgIDogT3BlcmF0aW9uS2V5cy5VUERBVEUsXG4gICAgICAgICAgICBzdGVwOiByLmNoYW5nZXNbci5jaGFuZ2VzLmxlbmd0aCAtIDFdLnJldixcbiAgICAgICAgICB9O1xuICAgICAgICB9KVxuICAgICAgICAucmVkdWNlKFxuICAgICAgICAgIChcbiAgICAgICAgICAgIGFjY3VtOiBSZWNvcmQ8XG4gICAgICAgICAgICAgIHN0cmluZyxcbiAgICAgICAgICAgICAgUmVjb3JkPFxuICAgICAgICAgICAgICAgIHN0cmluZyxcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICBpZHM6IFNldDxhbnk+O1xuICAgICAgICAgICAgICAgICAgc3RlcDogc3RyaW5nO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgPixcbiAgICAgICAgICAgIHJcbiAgICAgICAgICApID0+IHtcbiAgICAgICAgICAgIGlmICghcikgcmV0dXJuIGFjY3VtO1xuICAgICAgICAgICAgY29uc3QgeyB0YWJsZSwgaWQsIG9wZXJhdGlvbiwgc3RlcCB9ID0gciBhcyB7XG4gICAgICAgICAgICAgIHRhYmxlOiBzdHJpbmc7XG4gICAgICAgICAgICAgIGlkOiBzdHJpbmc7XG4gICAgICAgICAgICAgIG9wZXJhdGlvbjogT3BlcmF0aW9uS2V5cztcbiAgICAgICAgICAgICAgc3RlcDogc3RyaW5nO1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGlmICghYWNjdW1bdGFibGVdKSBhY2N1bVt0YWJsZV0gPSB7fTtcbiAgICAgICAgICAgIGlmICghYWNjdW1bdGFibGVdW29wZXJhdGlvbl0pXG4gICAgICAgICAgICAgIGFjY3VtW3RhYmxlXVtvcGVyYXRpb25dID0geyBpZHM6IG5ldyBTZXQoKSwgc3RlcDogc3RlcCB9O1xuICAgICAgICAgICAgYWNjdW1bdGFibGVdW29wZXJhdGlvbl0uaWRzLmFkZChpZCk7XG4gICAgICAgICAgICBhY2N1bVt0YWJsZV1bb3BlcmF0aW9uXS5zdGVwID0gc3RlcDtcbiAgICAgICAgICAgIHJldHVybiBhY2N1bTtcbiAgICAgICAgICB9LFxuICAgICAgICAgIHt9XG4gICAgICAgICk7XG5cbiAgICAgIGZvciAoY29uc3QgdGFibGUgb2YgT2JqZWN0LmtleXMoY2hhbmdlcykpIHtcbiAgICAgICAgZm9yIChjb25zdCBvcCBvZiBPYmplY3Qua2V5cyhjaGFuZ2VzW3RhYmxlXSkpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy51cGRhdGVPYnNlcnZlcnModGFibGUsIG9wLCBbXG4gICAgICAgICAgICAgIC4uLmNoYW5nZXNbdGFibGVdW29wXS5pZHMudmFsdWVzKCksXG4gICAgICAgICAgICBdKTtcbiAgICAgICAgICAgIHRoaXMub2JzZXJ2ZXJMYXN0VXBkYXRlID0gY2hhbmdlc1t0YWJsZV1bb3BdLnN0ZXA7XG4gICAgICAgICAgICBsb2cudmVyYm9zZShgT2JzZXJ2ZXIgcmVmcmVzaCBkaXNwYXRjaGVkIGJ5ICR7b3B9IGZvciAke3RhYmxlfWApO1xuICAgICAgICAgICAgbG9nLmRlYnVnKGBwa3M6ICR7QXJyYXkuZnJvbShjaGFuZ2VzW3RhYmxlXVtvcF0uaWRzLnZhbHVlcygpKX1gKTtcbiAgICAgICAgICB9IGNhdGNoIChlOiB1bmtub3duKSB7XG4gICAgICAgICAgICBsb2cuZXJyb3IoXG4gICAgICAgICAgICAgIGBGYWlsZWQgdG8gZGlzcGF0Y2ggb2JzZXJ2ZXIgcmVmcmVzaCBmb3IgJHt0YWJsZX0sIG9wICR7b3B9OiAke2V9YFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIEluaXRpYWxpemVzIHRoZSBkaXNwYXRjaGVyIGFuZCBzdWJzY3JpYmVzIHRvIGRhdGFiYXNlIGNoYW5nZXNcbiAgICogQHN1bW1hcnkgU2V0cyB1cCB0aGUgY29udGludW91cyBjaGFuZ2VzIGZlZWQgc3Vic2NyaXB0aW9uIHRvIHRoZSBOYW5vIGRhdGFiYXNlXG4gICAqIGFuZCBoYW5kbGVzIHJlY29ubmVjdGlvbiBhdHRlbXB0cyBpZiB0aGUgY29ubmVjdGlvbiBmYWlsc1xuICAgKiBAcmV0dXJuIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBzdWJzY3JpcHRpb24gaXMgZXN0YWJsaXNoZWRcbiAgICogQG1lcm1haWRcbiAgICogc2VxdWVuY2VEaWFncmFtXG4gICAqICAgcGFydGljaXBhbnQgRCBhcyBOYW5vRGlzcGF0Y2hcbiAgICogICBwYXJ0aWNpcGFudCBTIGFzIHN1YnNjcmliZVRvQ291Y2hcbiAgICogICBwYXJ0aWNpcGFudCBEQiBhcyBOYW5vIERhdGFiYXNlXG4gICAqICAgcGFydGljaXBhbnQgTCBhcyBMb2dnZXJcbiAgICogICBELT4+UzogQ2FsbCBzdWJzY3JpYmVUb0NvdWNoXG4gICAqICAgUy0+PlM6IENoZWNrIGFkYXB0ZXIgYW5kIG5hdGl2ZVxuICAgKiAgIGFsdCBObyBhZGFwdGVyIG9yIG5hdGl2ZVxuICAgKiAgICAgUy0tPj5TOiB0aHJvdyBJbnRlcm5hbEVycm9yXG4gICAqICAgZW5kXG4gICAqICAgUy0+PkRCOiBjaGFuZ2VzKG9wdGlvbnMsIGNoYW5nZUhhbmRsZXIpXG4gICAqICAgYWx0IFN1Y2Nlc3NcbiAgICogICAgIERCLS0+PlM6IFN1YnNjcmlwdGlvbiBlc3RhYmxpc2hlZFxuICAgKiAgICAgUy0tPj5EOiBQcm9taXNlIHJlc29sdmVzXG4gICAqICAgICBELT4+TDogTG9nIHN1Y2Nlc3NmdWwgc3Vic2NyaXB0aW9uXG4gICAqICAgZWxzZSBFcnJvclxuICAgKiAgICAgREItLT4+UzogRXJyb3JcbiAgICogICAgIFMtPj5TOiBJbmNyZW1lbnQgYXR0ZW1wdENvdW50ZXJcbiAgICogICAgIGFsdCBhdHRlbXB0Q291bnRlciA+IDNcbiAgICogICAgICAgUy0+Pkw6IExvZyBlcnJvclxuICAgKiAgICAgICBTLS0+PkQ6IFByb21pc2UgcmVqZWN0c1xuICAgKiAgICAgZWxzZSBhdHRlbXB0Q291bnRlciA8PSAzXG4gICAqICAgICAgIFMtPj5MOiBMb2cgcmV0cnlcbiAgICogICAgICAgUy0+PlM6IFdhaXQgdGltZW91dFxuICAgKiAgICAgICBTLT4+UzogUmVjdXJzaXZlIGNhbGwgdG8gc3Vic2NyaWJlVG9Db3VjaFxuICAgKiAgICAgZW5kXG4gICAqICAgZW5kXG4gICAqL1xuICBwcm90ZWN0ZWQgb3ZlcnJpZGUgYXN5bmMgaW5pdGlhbGl6ZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBsb2cgPSB0aGlzLmxvZy5mb3IodGhpcy5pbml0aWFsaXplKTtcbiAgICBjb25zdCBzdWJMb2cgPSBsb2cuZm9yKHN1YnNjcmliZVRvQ291Y2gpO1xuICAgIGFzeW5jIGZ1bmN0aW9uIHN1YnNjcmliZVRvQ291Y2godGhpczogTmFub0Rpc3BhdGNoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICBpZiAoIXRoaXMuYWRhcHRlciB8fCAhdGhpcy5uYXRpdmUpXG4gICAgICAgIHRocm93IG5ldyBJbnRlcm5hbEVycm9yKGBObyBhZGFwdGVyL25hdGl2ZSBvYnNlcnZlZCBmb3IgZGlzcGF0Y2hgKTtcblxuICAgICAgdHJ5IHtcbiAgICAgICAgdGhpcy5uYXRpdmUuY2hhbmdlcyhcbiAgICAgICAgICB7XG4gICAgICAgICAgICBmZWVkOiBcImNvbnRpbnVvdXNcIixcbiAgICAgICAgICAgIGluY2x1ZGVfZG9jczogZmFsc2UsXG4gICAgICAgICAgICBzaW5jZTogdGhpcy5vYnNlcnZlckxhc3RVcGRhdGUgfHwgXCJub3dcIixcbiAgICAgICAgICAgIHRpbWVvdXQ6IHRoaXMudGltZW91dCxcbiAgICAgICAgICB9LFxuICAgICAgICAgIHRoaXMuY2hhbmdlSGFuZGxlci5iaW5kKHRoaXMpIGFzIGFueVxuICAgICAgICApO1xuICAgICAgfSBjYXRjaCAoZTogdW5rbm93bikge1xuICAgICAgICBpZiAoKyt0aGlzLmF0dGVtcHRDb3VudGVyID4gMylcbiAgICAgICAgICByZXR1cm4gc3ViTG9nLmVycm9yKGBGYWlsZWQgdG8gc3Vic2NyaWJlIHRvIGNvdWNoZGIgY2hhbmdlczogJHtlfWApO1xuICAgICAgICBzdWJMb2cuaW5mbyhcbiAgICAgICAgICBgRmFpbGVkIHRvIHN1YnNjcmliZSB0byBjb3VjaGRiIGNoYW5nZXM6ICR7ZX0uIFJldHJ5aW5nIGluIDUgc2Vjb25kcy4uLmBcbiAgICAgICAgKTtcbiAgICAgICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgdGhpcy50aW1lb3V0KSk7XG4gICAgICAgIHJldHVybiBzdWJzY3JpYmVUb0NvdWNoLmNhbGwodGhpcyk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgc3Vic2NyaWJlVG9Db3VjaFxuICAgICAgLmNhbGwodGhpcylcbiAgICAgIC50aGVuKCgpID0+IHtcbiAgICAgICAgdGhpcy5sb2cuaW5mbyhgU3Vic2NyaWJlZCB0byBjb3VjaGRiIGNoYW5nZXNgKTtcbiAgICAgIH0pXG4gICAgICAuY2F0Y2goKGU6IHVua25vd24pID0+IHtcbiAgICAgICAgdGhyb3cgbmV3IEludGVybmFsRXJyb3IoYEZhaWxlZCB0byBzdWJzY3JpYmUgdG8gY291Y2hkYiBjaGFuZ2VzOiAke2V9YCk7XG4gICAgICB9KTtcbiAgfVxufVxuIl19