UNPKG

@decaf-ts/for-nano

Version:

decaf-ts persistence adapter for CouchDB via nano

214 lines 25.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NanoDispatch = void 0; const core_1 = require("@decaf-ts/core"); const db_decorators_1 = require("@decaf-ts/db-decorators"); const for_couchdb_1 = require("@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 */ class NanoDispatch extends core_1.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(for_couchdb_1.CouchDBKeys.SEPARATOR); return { table: table, id: id, operation: r.deleted ? db_decorators_1.OperationKeys.DELETE : r.changes[r.changes.length - 1].rev.split("-")[0] === "1" ? db_decorators_1.OperationKeys.CREATE : db_decorators_1.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 db_decorators_1.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 db_decorators_1.InternalError(`Failed to subscribe to couchdb changes: ${e}`); }); } } exports.NanoDispatch = NanoDispatch; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTmFub0Rpc3BhdGNoLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL05hbm9EaXNwYXRjaC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSx5Q0FBMEM7QUFPMUMsMkRBQXVFO0FBQ3ZFLHVEQUFvRDtBQUVwRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQ0c7QUFDSCxNQUFhLFlBQWEsU0FBUSxlQUE0QjtJQUc1RCxZQUFvQixVQUFVLElBQUk7UUFDaEMsS0FBSyxFQUFFLENBQUM7UUFEVSxZQUFPLEdBQVAsT0FBTyxDQUFPO1FBRDFCLG1CQUFjLEdBQVcsQ0FBQyxDQUFDO0lBR25DLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O09BOEJHO0lBQ08sS0FBSyxDQUFDLGFBQWEsQ0FDM0IsS0FBMEIsRUFDMUIsUUFBMEU7SUFDMUUsNkRBQTZEO0lBQzdELE9BQWE7UUFFYixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDN0MsSUFBSSxLQUFLO1lBQUUsT0FBTyxHQUFHLENBQUMsS0FBSyxDQUFDLDRCQUE0QixLQUFLLEVBQUUsQ0FBQyxDQUFDO1FBQ2pFLElBQUksQ0FBQztZQUNILFFBQVEsR0FBRyxDQUNULE9BQU8sUUFBUSxLQUFLLFFBQVE7Z0JBQzFCLENBQUMsQ0FBQyxRQUFRO3FCQUNMLEtBQUssQ0FBQyxJQUFJLENBQUM7cUJBQ1gsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO3FCQUNsQixHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7Z0JBQzlCLENBQUMsQ0FBQyxRQUFRLENBQ2dCLENBQUM7UUFDakMsQ0FBQztRQUFDLE9BQU8sQ0FBVSxFQUFFLENBQUM7WUFD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aXNwYXRjaCB9IGZyb20gXCJAZGVjYWYtdHMvY29yZVwiO1xuaW1wb3J0IHtcbiAgRGF0YWJhc2VDaGFuZ2VzUmVzcG9uc2UsXG4gIERhdGFiYXNlQ2hhbmdlc1Jlc3VsdEl0ZW0sXG4gIERvY3VtZW50U2NvcGUsXG4gIFJlcXVlc3RFcnJvcixcbn0gZnJvbSBcIm5hbm9cIjtcbmltcG9ydCB7IEludGVybmFsRXJyb3IsIE9wZXJhdGlvbktleXMgfSBmcm9tIFwiQGRlY2FmLXRzL2RiLWRlY29yYXRvcnNcIjtcbmltcG9ydCB7IENvdWNoREJLZXlzIH0gZnJvbSBcIkBkZWNhZi10cy9mb3ItY291Y2hkYlwiO1xuXG4vKipcbiAqIEBkZXNjcmlwdGlvbiBEaXNwYXRjaGVyIGZvciBOYW5vIGRhdGFiYXNlIGNoYW5nZSBldmVudHNcbiAqIEBzdW1tYXJ5IEhhbmRsZXMgdGhlIHN1YnNjcmlwdGlvbiB0byBhbmQgcHJvY2Vzc2luZyBvZiBkYXRhYmFzZSBjaGFuZ2UgZXZlbnRzIGZyb20gYSBOYW5vIGRhdGFiYXNlLFxuICogbm90aWZ5aW5nIG9ic2VydmVycyB3aGVuIGRvY3VtZW50cyBhcmUgY3JlYXRlZCwgdXBkYXRlZCwgb3IgZGVsZXRlZFxuICogQHRlbXBsYXRlIERvY3VtZW50U2NvcGUgLSBUaGUgTmFubyBkb2N1bWVudCBzY29wZSB0eXBlXG4gKiBAcGFyYW0ge251bWJlcn0gW3RpbWVvdXQ9NTAwMF0gLSBUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgY2hhbmdlIGZlZWQgcmVxdWVzdHNcbiAqIEBjbGFzcyBOYW5vRGlzcGF0Y2hcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiAvLyBDcmVhdGUgYSBkaXNwYXRjaGVyIGZvciBhIE5hbm8gZGF0YWJhc2VcbiAqIGNvbnN0IGRiID0gc2VydmVyLmRiLnVzZSgnbXlfZGF0YWJhc2UnKTtcbiAqIGNvbnN0IGFkYXB0ZXIgPSBuZXcgTmFub0FkYXB0ZXIoZGIpO1xuICogY29uc3QgZGlzcGF0Y2ggPSBuZXcgTmFub0Rpc3BhdGNoKCk7XG4gKlxuICogLy8gVGhlIGRpc3BhdGNoZXIgd2lsbCBhdXRvbWF0aWNhbGx5IHN1YnNjcmliZSB0byBjaGFuZ2VzXG4gKiAvLyBhbmQgbm90aWZ5IG9ic2VydmVycyB3aGVuIGRvY3VtZW50cyBjaGFuZ2VcbiAqIGBgYFxuICogQG1lcm1haWRcbiAqIGNsYXNzRGlhZ3JhbVxuICogICBjbGFzcyBEaXNwYXRjaCB7XG4gKiAgICAgK2luaXRpYWxpemUoKVxuICogICAgICt1cGRhdGVPYnNlcnZlcnMoKVxuICogICB9XG4gKiAgIGNsYXNzIE5hbm9EaXNwYXRjaCB7XG4gKiAgICAgLW9ic2VydmVyTGFzdFVwZGF0ZT86IHN0cmluZ1xuICogICAgIC1hdHRlbXB0Q291bnRlcjogbnVtYmVyXG4gKiAgICAgLXRpbWVvdXQ6IG51bWJlclxuICogICAgICtjb25zdHJ1Y3Rvcih0aW1lb3V0KVxuICogICAgICNjaGFuZ2VIYW5kbGVyKClcbiAqICAgICAjaW5pdGlhbGl6ZSgpXG4gKiAgIH1cbiAqICAgRGlzcGF0Y2ggPHwtLSBOYW5vRGlzcGF0Y2hcbiAqL1xuZXhwb3J0IGNsYXNzIE5hbm9EaXNwYXRjaCBleHRlbmRzIERpc3BhdGNoPERvY3VtZW50U2NvcGU8YW55Pj4ge1xuICBwcml2YXRlIG9ic2VydmVyTGFzdFVwZGF0ZT86IHN0cmluZztcbiAgcHJpdmF0ZSBhdHRlbXB0Q291bnRlcjogbnVtYmVyID0gMDtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSB0aW1lb3V0ID0gNTAwMCkge1xuICAgIHN1cGVyKCk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uIFByb2Nlc3NlcyBkYXRhYmFzZSBjaGFuZ2UgZXZlbnRzXG4gICAqIEBzdW1tYXJ5IEhhbmRsZXMgdGhlIHJlc3BvbnNlIGZyb20gdGhlIE5hbm8gY2hhbmdlcyBmZWVkLCBwcm9jZXNzZXMgdGhlIGNoYW5nZXMsXG4gICAqIGFuZCBub3RpZmllcyBvYnNlcnZlcnMgYWJvdXQgZG9jdW1lbnQgY2hhbmdlc1xuICAgKiBAcGFyYW0ge1JlcXVlc3RFcnJvciB8IG51bGx9IGVycm9yIC0gRXJyb3Igb2JqZWN0IGlmIHRoZSByZXF1ZXN0IGZhaWxlZFxuICAgKiBAcGFyYW0gcmVzcG9uc2UgLSBUaGUgY2hhbmdlcyByZXNwb25zZSBmcm9tIE5hbm9cbiAgICogQHBhcmFtIHthbnl9IFtoZWFkZXJzXSAtIFJlc3BvbnNlIGhlYWRlcnMgKHVudXNlZClcbiAgICogQHJldHVybiB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiBhbGwgY2hhbmdlcyBoYXZlIGJlZW4gcHJvY2Vzc2VkXG4gICAqIEBtZXJtYWlkXG4gICAqIHNlcXVlbmNlRGlhZ3JhbVxuICAgKiAgIHBhcnRpY2lwYW50IEQgYXMgTmFub0Rpc3BhdGNoXG4gICAqICAgcGFydGljaXBhbnQgTCBhcyBMb2dnZXJcbiAgICogICBwYXJ0aWNpcGFudCBPIGFzIE9ic2VydmVyc1xuICAgKiAgIE5vdGUgb3ZlciBEOiBSZWNlaXZlIGNoYW5nZXMgZnJvbSBOYW5vXG4gICAqICAgYWx0IEVycm9yIGluIHJlc3BvbnNlXG4gICAqICAgICBELT4+TDogTG9nIGVycm9yXG4gICAqICAgICBELS0+PkQ6IFJldHVybiBlYXJseVxuICAgKiAgIGVuZFxuICAgKiAgIGFsdCBSZXNwb25zZSBpcyBzdHJpbmdcbiAgICogICAgIEQtPj5EOiBQYXJzZSBKU09OIGZyb20gc3RyaW5nXG4gICAqICAgZW5kXG4gICAqICAgRC0+PkQ6IFByb2Nlc3MgY2hhbmdlc1xuICAgKiAgIEQtPj5EOiBHcm91cCBjaGFuZ2VzIGJ5IHRhYmxlIGFuZCBvcGVyYXRpb25cbiAgICogICBsb29wIEZvciBlYWNoIHRhYmxlXG4gICAqICAgICBsb29wIEZvciBlYWNoIG9wZXJhdGlvblxuICAgKiAgICAgICBELT4+TzogdXBkYXRlT2JzZXJ2ZXJzKHRhYmxlLCBvcGVyYXRpb24sIGlkcylcbiAgICogICAgICAgRC0+PkQ6IFVwZGF0ZSBvYnNlcnZlckxhc3RVcGRhdGVcbiAgICogICAgICAgRC0+Pkw6IExvZyBzdWNjZXNzZnVsIGRpc3BhdGNoXG4gICAqICAgICBlbmRcbiAgICogICBlbmRcbiAgICovXG4gIHByb3RlY3RlZCBhc3luYyBjaGFuZ2VIYW5kbGVyKFxuICAgIGVycm9yOiBSZXF1ZXN0RXJyb3IgfCBudWxsLFxuICAgIHJlc3BvbnNlOiAoRGF0YWJhc2VDaGFuZ2VzUmVzcG9uc2UgfCBEYXRhYmFzZUNoYW5nZXNSZXN1bHRJdGVtKVtdIHwgc3RyaW5nLFxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tdW51c2VkLXZhcnNcbiAgICBoZWFkZXJzPzogYW55XG4gICkge1xuICAgIGNvbnN0IGxvZyA9IHRoaXMubG9nLmZvcih0aGlzLmNoYW5nZUhhbmRsZXIpO1xuICAgIGlmIChlcnJvcikgcmV0dXJuIGxvZy5lcnJvcihgRXJyb3IgaW4gY2hhbmdlIHJlcXVlc3Q6ICR7ZXJyb3J9YCk7XG4gICAgdHJ5IHtcbiAgICAgIHJlc3BvbnNlID0gKFxuICAgICAgICB0eXBlb2YgcmVzcG9uc2UgPT09IFwic3RyaW5nXCJcbiAgICAgICAgICA/IHJlc3BvbnNlXG4gICAgICAgICAgICAgIC5zcGxpdChcIlxcblwiKVxuICAgICAgICAgICAgICAuZmlsdGVyKChyKSA9PiAhIXIpXG4gICAgICAgICAgICAgIC5tYXAoKHIpID0+IEpTT04ucGFyc2UocikpXG4gICAgICAgICAgOiByZXNwb25zZVxuICAgICAgKSBhcyBEYXRhYmFzZUNoYW5nZXNSZXNwb25zZVtdO1xuICAgIH0gY2F0Y2ggKGU6IHVua25vd24pIHtcbiAgICAgIHJldHVybiBsb2cuZXJyb3IoYEVycm9yIHBhcnNpbmcgY291Y2hkYiBjaGFuZ2UgZmVlZDogJHtlfWApO1xuICAgIH1cbiAgICBjb25zdCBjb3VudCA9IHJlc3BvbnNlLmxlbmd0aDtcbiAgICBpZiAoY291bnQgPiAwKSB7XG4gICAgICBsb2cuZGVidWcoYFJlY2VpdmVkICR7Y291bnR9IGNoYW5nZXMuIHByb2Nlc3NpbmcuLi5gKTtcbiAgICAgIGNvbnN0IGNoYW5nZXMgPSByZXNwb25zZVxuICAgICAgICAubWFwKChyZWMsIGkpID0+IHtcbiAgICAgICAgICBpZiAoaSA9PT0gY291bnQgLSAxKSB7XG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgIHRoaXMub2JzZXJ2ZXJMYXN0VXBkYXRlID09PVxuICAgICAgICAgICAgICAocmVjIGFzIERhdGFiYXNlQ2hhbmdlc1Jlc3BvbnNlKS5sYXN0X3NlcVxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgICBsb2cuZXJyb3IoXG4gICAgICAgICAgICAgICAgYEludmFsaWQgbGFzdCB1cGRhdGUgY2hlY2s6ICR7dGhpcy5vYnNlcnZlckxhc3RVcGRhdGV9ICE9PSAkeyhyZWMgYXMgRGF0YWJhc2VDaGFuZ2VzUmVzcG9uc2UpLmxhc3Rfc2VxfWBcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3QgciA9IHJlYyBhcyBEYXRhYmFzZUNoYW5nZXNSZXN1bHRJdGVtO1xuICAgICAgICAgIGNvbnN0IFt0YWJsZSwgaWRdID0gci5pZC5zcGxpdChDb3VjaERCS2V5cy5TRVBBUkFUT1IpO1xuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICB0YWJsZTogdGFibGUsXG4gICAgICAgICAgICBpZDogaWQsXG4gICAgICAgICAgICBvcGVyYXRpb246IHIuZGVsZXRlZFxuICAgICAgICAgICAgICA/IE9wZXJhdGlvbktleXMuREVMRVRFXG4gICAgICAgICAgICAgIDogci5jaGFuZ2VzW3IuY2hhbmdlcy5sZW5ndGggLSAxXS5yZXYuc3BsaXQoXCItXCIpWzBdID09PSBcIjFcIlxuICAgICAgICAgICAgICAgID8gT3BlcmF0aW9uS2V5cy5DUkVBVEVcbiAgICAgICAgICAgICAgICA6IE9wZXJhdGlvbktleXMuVVBEQVRFLFxuICAgICAgICAgICAgc3RlcDogci5jaGFuZ2VzW3IuY2hhbmdlcy5sZW5ndGggLSAxXS5yZXYsXG4gICAgICAgICAgfTtcbiAgICAgICAgfSlcbiAgICAgICAgLnJlZHVjZShcbiAgICAgICAgICAoXG4gICAgICAgICAgICBhY2N1bTogUmVjb3JkPFxuICAgICAgICAgICAgICBzdHJpbmcsXG4gICAgICAgICAgICAgIFJlY29yZDxcbiAgICAgICAgICAgICAgICBzdHJpbmcsXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgaWRzOiBTZXQ8YW55PjtcbiAgICAgICAgICAgICAgICAgIHN0ZXA6IHN0cmluZztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgID5cbiAgICAgICAgICAgID4sXG4gICAgICAgICAgICByXG4gICAgICAgICAgKSA9PiB7XG4gICAgICAgICAgICBpZiAoIXIpIHJldHVybiBhY2N1bTtcbiAgICAgICAgICAgIGNvbnN0IHsgdGFibGUsIGlkLCBvcGVyYXRpb24sIHN0ZXAgfSA9IHIgYXMge1xuICAgICAgICAgICAgICB0YWJsZTogc3RyaW5nO1xuICAgICAgICAgICAgICBpZDogc3RyaW5nO1xuICAgICAgICAgICAgICBvcGVyYXRpb246IE9wZXJhdGlvbktleXM7XG4gICAgICAgICAgICAgIHN0ZXA6IHN0cmluZztcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICBpZiAoIWFjY3VtW3RhYmxlXSkgYWNjdW1bdGFibGVdID0ge307XG4gICAgICAgICAgICBpZiAoIWFjY3VtW3RhYmxlXVtvcGVyYXRpb25dKVxuICAgICAgICAgICAgICBhY2N1bVt0YWJsZV1bb3BlcmF0aW9uXSA9IHsgaWRzOiBuZXcgU2V0KCksIHN0ZXA6IHN0ZXAgfTtcbiAgICAgICAgICAgIGFjY3VtW3RhYmxlXVtvcGVyYXRpb25dLmlkcy5hZGQoaWQpO1xuICAgICAgICAgICAgYWNjdW1bdGFibGVdW29wZXJhdGlvbl0uc3RlcCA9IHN0ZXA7XG4gICAgICAgICAgICByZXR1cm4gYWNjdW07XG4gICAgICAgICAgfSxcbiAgICAgICAgICB7fVxuICAgICAgICApO1xuXG4gICAgICBmb3IgKGNvbnN0IHRhYmxlIG9mIE9iamVjdC5rZXlzKGNoYW5nZXMpKSB7XG4gICAgICAgIGZvciAoY29uc3Qgb3Agb2YgT2JqZWN0LmtleXMoY2hhbmdlc1t0YWJsZV0pKSB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMudXBkYXRlT2JzZXJ2ZXJzKHRhYmxlLCBvcCwgW1xuICAgICAgICAgICAgICAuLi5jaGFuZ2VzW3RhYmxlXVtvcF0uaWRzLnZhbHVlcygpLFxuICAgICAgICAgICAgXSk7XG4gICAgICAgICAgICB0aGlzLm9ic2VydmVyTGFzdFVwZGF0ZSA9IGNoYW5nZXNbdGFibGVdW29wXS5zdGVwO1xuICAgICAgICAgICAgbG9nLnZlcmJvc2UoYE9ic2VydmVyIHJlZnJlc2ggZGlzcGF0Y2hlZCBieSAke29wfSBmb3IgJHt0YWJsZX1gKTtcbiAgICAgICAgICAgIGxvZy5kZWJ1ZyhgcGtzOiAke0FycmF5LmZyb20oY2hhbmdlc1t0YWJsZV1bb3BdLmlkcy52YWx1ZXMoKSl9YCk7XG4gICAgICAgICAgfSBjYXRjaCAoZTogdW5rbm93bikge1xuICAgICAgICAgICAgbG9nLmVycm9yKFxuICAgICAgICAgICAgICBgRmFpbGVkIHRvIGRpc3BhdGNoIG9ic2VydmVyIHJlZnJlc2ggZm9yICR7dGFibGV9LCBvcCAke29wfTogJHtlfWBcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBJbml0aWFsaXplcyB0aGUgZGlzcGF0Y2hlciBhbmQgc3Vic2NyaWJlcyB0byBkYXRhYmFzZSBjaGFuZ2VzXG4gICAqIEBzdW1tYXJ5IFNldHMgdXAgdGhlIGNvbnRpbnVvdXMgY2hhbmdlcyBmZWVkIHN1YnNjcmlwdGlvbiB0byB0aGUgTmFubyBkYXRhYmFzZVxuICAgKiBhbmQgaGFuZGxlcyByZWNvbm5lY3Rpb24gYXR0ZW1wdHMgaWYgdGhlIGNvbm5lY3Rpb24gZmFpbHNcbiAgICogQHJldHVybiB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc3Vic2NyaXB0aW9uIGlzIGVzdGFibGlzaGVkXG4gICAqIEBtZXJtYWlkXG4gICAqIHNlcXVlbmNlRGlhZ3JhbVxuICAgKiAgIHBhcnRpY2lwYW50IEQgYXMgTmFub0Rpc3BhdGNoXG4gICAqICAgcGFydGljaXBhbnQgUyBhcyBzdWJzY3JpYmVUb0NvdWNoXG4gICAqICAgcGFydGljaXBhbnQgREIgYXMgTmFubyBEYXRhYmFzZVxuICAgKiAgIHBhcnRpY2lwYW50IEwgYXMgTG9nZ2VyXG4gICAqICAgRC0+PlM6IENhbGwgc3Vic2NyaWJlVG9Db3VjaFxuICAgKiAgIFMtPj5TOiBDaGVjayBhZGFwdGVyIGFuZCBuYXRpdmVcbiAgICogICBhbHQgTm8gYWRhcHRlciBvciBuYXRpdmVcbiAgICogICAgIFMtLT4+UzogdGhyb3cgSW50ZXJuYWxFcnJvclxuICAgKiAgIGVuZFxuICAgKiAgIFMtPj5EQjogY2hhbmdlcyhvcHRpb25zLCBjaGFuZ2VIYW5kbGVyKVxuICAgKiAgIGFsdCBTdWNjZXNzXG4gICAqICAgICBEQi0tPj5TOiBTdWJzY3JpcHRpb24gZXN0YWJsaXNoZWRcbiAgICogICAgIFMtLT4+RDogUHJvbWlzZSByZXNvbHZlc1xuICAgKiAgICAgRC0+Pkw6IExvZyBzdWNjZXNzZnVsIHN1YnNjcmlwdGlvblxuICAgKiAgIGVsc2UgRXJyb3JcbiAgICogICAgIERCLS0+PlM6IEVycm9yXG4gICAqICAgICBTLT4+UzogSW5jcmVtZW50IGF0dGVtcHRDb3VudGVyXG4gICAqICAgICBhbHQgYXR0ZW1wdENvdW50ZXIgPiAzXG4gICAqICAgICAgIFMtPj5MOiBMb2cgZXJyb3JcbiAgICogICAgICAgUy0tPj5EOiBQcm9taXNlIHJlamVjdHNcbiAgICogICAgIGVsc2UgYXR0ZW1wdENvdW50ZXIgPD0gM1xuICAgKiAgICAgICBTLT4+TDogTG9nIHJldHJ5XG4gICAqICAgICAgIFMtPj5TOiBXYWl0IHRpbWVvdXRcbiAgICogICAgICAgUy0+PlM6IFJlY3Vyc2l2ZSBjYWxsIHRvIHN1YnNjcmliZVRvQ291Y2hcbiAgICogICAgIGVuZFxuICAgKiAgIGVuZFxuICAgKi9cbiAgcHJvdGVjdGVkIG92ZXJyaWRlIGFzeW5jIGluaXRpYWxpemUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgbG9nID0gdGhpcy5sb2cuZm9yKHRoaXMuaW5pdGlhbGl6ZSk7XG4gICAgY29uc3Qgc3ViTG9nID0gbG9nLmZvcihzdWJzY3JpYmVUb0NvdWNoKTtcbiAgICBhc3luYyBmdW5jdGlvbiBzdWJzY3JpYmVUb0NvdWNoKHRoaXM6IE5hbm9EaXNwYXRjaCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgaWYgKCF0aGlzLmFkYXB0ZXIgfHwgIXRoaXMubmF0aXZlKVxuICAgICAgICB0aHJvdyBuZXcgSW50ZXJuYWxFcnJvcihgTm8gYWRhcHRlci9uYXRpdmUgb2JzZXJ2ZWQgZm9yIGRpc3BhdGNoYCk7XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIHRoaXMubmF0aXZlLmNoYW5nZXMoXG4gICAgICAgICAge1xuICAgICAgICAgICAgZmVlZDogXCJjb250aW51b3VzXCIsXG4gICAgICAgICAgICBpbmNsdWRlX2RvY3M6IGZhbHNlLFxuICAgICAgICAgICAgc2luY2U6IHRoaXMub2JzZXJ2ZXJMYXN0VXBkYXRlIHx8IFwibm93XCIsXG4gICAgICAgICAgICB0aW1lb3V0OiB0aGlzLnRpbWVvdXQsXG4gICAgICAgICAgfSxcbiAgICAgICAgICB0aGlzLmNoYW5nZUhhbmRsZXIuYmluZCh0aGlzKSBhcyBhbnlcbiAgICAgICAgKTtcbiAgICAgIH0gY2F0Y2ggKGU6IHVua25vd24pIHtcbiAgICAgICAgaWYgKCsrdGhpcy5hdHRlbXB0Q291bnRlciA+IDMpXG4gICAgICAgICAgcmV0dXJuIHN1YkxvZy5lcnJvcihgRmFpbGVkIHRvIHN1YnNjcmliZSB0byBjb3VjaGRiIGNoYW5nZXM6ICR7ZX1gKTtcbiAgICAgICAgc3ViTG9nLmluZm8oXG4gICAgICAgICAgYEZhaWxlZCB0byBzdWJzY3JpYmUgdG8gY291Y2hkYiBjaGFuZ2VzOiAke2V9LiBSZXRyeWluZyBpbiA1IHNlY29uZHMuLi5gXG4gICAgICAgICk7XG4gICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIHRoaXMudGltZW91dCkpO1xuICAgICAgICByZXR1cm4gc3Vic2NyaWJlVG9Db3VjaC5jYWxsKHRoaXMpO1xuICAgICAgfVxuICAgIH1cblxuICAgIHN1YnNjcmliZVRvQ291Y2hcbiAgICAgIC5jYWxsKHRoaXMpXG4gICAgICAudGhlbigoKSA9PiB7XG4gICAgICAgIHRoaXMubG9nLmluZm8oYFN1YnNjcmliZWQgdG8gY291Y2hkYiBjaGFuZ2VzYCk7XG4gICAgICB9KVxuICAgICAgLmNhdGNoKChlOiB1bmtub3duKSA9PiB7XG4gICAgICAgIHRocm93IG5ldyBJbnRlcm5hbEVycm9yKGBGYWlsZWQgdG8gc3Vic2NyaWJlIHRvIGNvdWNoZGIgY2hhbmdlczogJHtlfWApO1xuICAgICAgfSk7XG4gIH1cbn1cbiJdfQ==