@decaf-ts/for-nano
Version:
decaf-ts persistence adapter for CouchDB via nano
210 lines • 25.8 kB
JavaScript
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