UNPKG

mobx-keystone-yjs

Version:

Yjs bindings for mobx-keystone

566 lines 66.7 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); import { createAtom, reaction, observe, computed, action, runInAction } from "mobx"; import { createContext, types, Model, tProp, frozen, getParentToChildPath, onSnapshot, model, modelSnapshotOutWithMetadata, applyPatches, onPatches, onGlobalPatches, fromSnapshot } from "mobx-keystone"; import * as Y from "yjs"; function __decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; class MobxKeystoneYjsError extends Error { constructor(msg) { super(msg); Object.setPrototypeOf(this, MobxKeystoneYjsError.prototype); } } function failure(msg) { return new MobxKeystoneYjsError(msg); } const yjsBindingContext = createContext(void 0); const yjsCollectionAtoms = /* @__PURE__ */ new WeakMap(); const getYjsCollectionAtom = (yjsCollection) => { return yjsCollectionAtoms.get(yjsCollection); }; const getOrCreateYjsCollectionAtom = (yjsCollection) => { let atom = yjsCollectionAtoms.get(yjsCollection); if (!atom) { atom = createAtom(`yjsCollectionAtom`); yjsCollectionAtoms.set(yjsCollection, atom); } return atom; }; function resolveYjsPath(yjsObject, path) { let currentYjsObject = yjsObject; path.forEach((pathPart, i) => { if (currentYjsObject instanceof Y.Map) { getOrCreateYjsCollectionAtom(currentYjsObject).reportObserved(); const key = String(pathPart); currentYjsObject = currentYjsObject.get(key); } else if (currentYjsObject instanceof Y.Array) { getOrCreateYjsCollectionAtom(currentYjsObject).reportObserved(); const key = Number(pathPart); currentYjsObject = currentYjsObject.get(key); } else { throw failure(`Y.Map or Y.Array was expected at path ${JSON.stringify(path.slice(0, i))} in order to resolve path ${JSON.stringify(path)}, but got ${currentYjsObject} instead`); } }); return currentYjsObject; } const deltaListType = types.array(types.frozen(types.unchecked())); const yjsTextModelId = "mobx-keystone-yjs/YjsTextModel"; let YjsTextModel = class YjsTextModel2 extends Model({ deltaList: tProp(deltaListType, () => []) }) { constructor() { super(...arguments); /** * Atom that gets changed when the associated Y.js text changes. */ __publicField(this, "yjsTextChangedAtom", createAtom("yjsTextChangedAtom")); } /** * Helper function to create a YjsTextModel instance with a simple text. */ static withText(text) { return new DecoratedYjsTextModel({ deltaList: [ frozen([ { insert: text } ]) ] }); } /** * The Y.js path from the bound object to the YjsTextModel instance. */ get _yjsObjectPath() { const ctx = yjsBindingContext.get(this); if ((ctx == null ? void 0 : ctx.boundObject) == null) { throw failure("the YjsTextModel instance must be part of a bound object before it can be accessed"); } const path = getParentToChildPath(ctx.boundObject, this); if (!path) { throw failure("a path from the bound object to the YjsTextModel instance is not available"); } return path; } /** * The Yjs.Text object present at this mobx-keystone node's path. */ get _yjsObjectAtPath() { const path = this._yjsObjectPath; const ctx = yjsBindingContext.get(this); return resolveYjsPath(ctx.yjsObject, path); } /** * The Yjs.Text object represented by this mobx-keystone node. */ get yjsText() { const yjsObject = this._yjsObjectAtPath; if (!(yjsObject instanceof Y.Text)) { throw failure(`Y.Text was expected at path ${JSON.stringify(this._yjsObjectPath)}`); } return yjsObject; } /** * The text value of the Yjs.Text object. * Shortcut for `yjsText.toString()`, but computed. */ get text() { this.yjsTextChangedAtom.reportObserved(); return this.yjsText.toString(); } onInit() { const shouldReplicateToYjs = (ctx) => { return !!ctx && !!ctx.boundObject && !ctx.isApplyingYjsChangesToMobxKeystone; }; let reapplyDeltasToYjsText = false; const newDeltas = []; let disposeObserveDeltaList; const disposeReactionToDeltaListRefChange = reaction(() => this.$.deltaList, (deltaList) => { disposeObserveDeltaList == null ? void 0 : disposeObserveDeltaList(); disposeObserveDeltaList = void 0; disposeObserveDeltaList = observe(deltaList, (change) => { if (reapplyDeltasToYjsText) { return; } if (!shouldReplicateToYjs(yjsBindingContext.get(this))) { return; } if (change.type === "splice" && change.removedCount === 0 && change.addedCount > 0 && change.index === this.deltaList.length) { newDeltas.push(...change.added); } else { reapplyDeltasToYjsText = true; } }); }, { fireImmediately: true }); const disposeOnSnapshot = onSnapshot(this, () => { try { if (reapplyDeltasToYjsText) { const ctx = yjsBindingContext.get(this); if (shouldReplicateToYjs(ctx)) { const { yjsText } = this; ctx.yjsDoc.transact(() => { if (yjsText.length > 0) { yjsText.delete(0, yjsText.length); } this.deltaList.forEach((frozenDeltas) => { yjsText.applyDelta(frozenDeltas.data); }); }, ctx.yjsOrigin); } } else if (newDeltas.length > 0) { const ctx = yjsBindingContext.get(this); if (shouldReplicateToYjs(ctx)) { const { yjsText } = this; ctx.yjsDoc.transact(() => { newDeltas.forEach((frozenDeltas) => { yjsText.applyDelta(frozenDeltas.data); }); }, ctx.yjsOrigin); } } } finally { reapplyDeltasToYjsText = false; newDeltas.length = 0; } }); const diposeYjsTextChangedAtom = hookYjsTextChangedAtom(() => this.yjsText, this.yjsTextChangedAtom); return () => { disposeOnSnapshot(); disposeReactionToDeltaListRefChange(); disposeObserveDeltaList == null ? void 0 : disposeObserveDeltaList(); disposeObserveDeltaList = void 0; diposeYjsTextChangedAtom(); }; } }; __decorate([ computed ], YjsTextModel.prototype, "_yjsObjectPath", null); __decorate([ computed ], YjsTextModel.prototype, "_yjsObjectAtPath", null); __decorate([ computed ], YjsTextModel.prototype, "yjsText", null); __decorate([ computed ], YjsTextModel.prototype, "text", null); YjsTextModel = __decorate([ model(yjsTextModelId) ], YjsTextModel); const DecoratedYjsTextModel = YjsTextModel; function hookYjsTextChangedAtom(getYjsText, textChangedAtom) { let disposeObserveYjsText; const observeFn = () => { textChangedAtom.reportChanged(); }; const disposeReactionToYTextChange = reaction(() => { try { return getYjsText(); } catch { return void 0; } }, (yjsText) => { disposeObserveYjsText == null ? void 0 : disposeObserveYjsText(); disposeObserveYjsText = void 0; if (yjsText) { yjsText.observe(observeFn); disposeObserveYjsText = () => { yjsText.unobserve(observeFn); }; } textChangedAtom.reportChanged(); }, { fireImmediately: true }); return () => { disposeReactionToYTextChange(); disposeObserveYjsText == null ? void 0 : disposeObserveYjsText(); disposeObserveYjsText = void 0; }; } function isPlainPrimitive(v) { const t = typeof v; return t === "string" || t === "number" || t === "boolean" || v === null || v === void 0; } function isPlainArray(v) { return Array.isArray(v); } function isPlainObject(v) { return !isPlainArray(v) && typeof v === "object" && v !== null; } function convertJsonToYjsData(v) { return runInAction(() => { if (isPlainPrimitive(v)) { return v; } if (isPlainArray(v)) { const arr = new Y.Array(); applyJsonArrayToYArray(arr, v); return arr; } if (isPlainObject(v)) { if (v.$frozen === true) { return v; } if (v.$modelType === yjsTextModelId) { const text = new Y.Text(); const yjsTextModel = v; yjsTextModel.deltaList.forEach((frozenDeltas) => { text.applyDelta(frozenDeltas.data); }); return text; } const map = new Y.Map(); applyJsonObjectToYMap(map, v); return map; } throw new Error(`unsupported value type: ${v}`); }); } const applyJsonArrayToYArray = action((dest, source) => { dest.push(source.map(convertJsonToYjsData)); }); const applyJsonObjectToYMap = action((dest, source) => { Object.entries(source).forEach(([k, v]) => { dest.set(k, convertJsonToYjsData(v)); }); }); function applyMobxKeystonePatchToYjsObject(patch, yjs) { if (patch.path.length > 1) { const [key, ...rest] = patch.path; if (yjs instanceof Y.Map) { const child = yjs.get(String(key)); if (child === void 0) { throw failure(`invalid patch path, key "${key}" not found in Yjs map - patch: ${JSON.stringify(patch)}`); } applyMobxKeystonePatchToYjsObject({ ...patch, path: rest }, child); } else if (yjs instanceof Y.Array) { const child = yjs.get(Number(key)); if (child === void 0) { throw failure(`invalid patch path, key "${key}" not found in Yjs array - patch: ${JSON.stringify(patch)}`); } applyMobxKeystonePatchToYjsObject({ ...patch, path: rest }, child); } else if (yjs instanceof Y.Text) ; else { throw failure(`invalid patch path, key "${key}" not found in unknown Yjs object - patch: ${JSON.stringify(patch)}`); } } else if (patch.path.length === 1) { if (yjs instanceof Y.Map) { const key = String(patch.path[0]); switch (patch.op) { case "add": case "replace": { yjs.set(key, convertJsonToYjsData(patch.value)); break; } case "remove": { yjs.delete(key); break; } default: { throw failure(`invalid patch operation for map`); } } } else if (yjs instanceof Y.Array) { const key = patch.path[0]; switch (patch.op) { case "replace": { if (key === "length") { const newLength = patch.value; if (yjs.length > newLength) { const toDelete = yjs.length - newLength; yjs.delete(newLength, toDelete); } else if (yjs.length < patch.value) { const toInsert = patch.value - yjs.length; yjs.insert(yjs.length, Array.from({ length: toInsert }).fill(void 0)); } } else { yjs.delete(Number(key)); yjs.insert(Number(key), [convertJsonToYjsData(patch.value)]); } break; } case "add": { yjs.insert(Number(key), [convertJsonToYjsData(patch.value)]); break; } case "remove": { yjs.delete(Number(key)); break; } default: { throw failure(`invalid patch operation for array`); } } } else if (yjs instanceof Y.Text) ; else { throw failure(`invalid patch path, the Yjs object is of an unkown type, so key "${String(patch.path[0])}" cannot be found in it`); } } else { throw failure(`invalid patch path, it cannot be empty`); } } const convertYjsDataToJson = action((yjsData) => { if (yjsData instanceof Y.Array) { return yjsData.map((v) => convertYjsDataToJson(v)); } if (yjsData instanceof Y.Map) { const obj = {}; yjsData.forEach((v, k) => { obj[k] = convertYjsDataToJson(v); }); return obj; } if (yjsData instanceof Y.Text) { const deltas = yjsData.toDelta(); return modelSnapshotOutWithMetadata(YjsTextModel, { deltaList: deltas.length > 0 ? [{ $frozen: true, data: deltas }] : [] }); } return yjsData; }); function convertYjsEventToPatches(event) { const patches = []; if (event instanceof Y.YMapEvent) { const source = event.target; event.changes.keys.forEach((change, key) => { const path = [...event.path, key]; switch (change.action) { case "add": patches.push({ op: "add", path, value: toPlainValue(source.get(key)) }); break; case "update": patches.push({ op: "replace", path, value: toPlainValue(source.get(key)) }); break; case "delete": patches.push({ op: "remove", path }); break; default: throw failure(`unsupported Yjs map event action: ${change.action}`); } }); } else if (event instanceof Y.YArrayEvent) { let retain = 0; event.changes.delta.forEach((change) => { if (change.retain) { retain += change.retain; } if (change.delete) { const path = [...event.path, retain]; for (let i = 0; i < change.delete; i++) { patches.push({ op: "remove", path }); } } if (change.insert) { const newValues = Array.isArray(change.insert) ? change.insert : [change.insert]; newValues.forEach((v) => { const path = [...event.path, retain]; patches.push({ op: "add", path, value: toPlainValue(v) }); retain++; }); } }); } else if (event instanceof Y.YTextEvent) { const path = [ ...event.path, "deltaList", -1 /* last item */ ]; patches.push({ op: "add", path, value: { $frozen: true, data: event.delta } }); } return patches; } function toPlainValue(v) { if (v instanceof Y.Map || v instanceof Y.Array) { return v.toJSON(); } else { return v; } } function bindYjsToMobxKeystone({ yjsDoc, yjsObject, mobxKeystoneType }) { const yjsOrigin = Symbol("bindYjsToMobxKeystoneTransactionOrigin"); let applyingYjsChangesToMobxKeystone = 0; const bindingContext = { yjsDoc, yjsObject, mobxKeystoneType, yjsOrigin, boundObject: void 0, // not yet created get isApplyingYjsChangesToMobxKeystone() { return applyingYjsChangesToMobxKeystone > 0; } }; const yjsJson = convertYjsDataToJson(yjsObject); const initializationGlobalPatches = []; const createBoundObject = () => { const disposeOnGlobalPatches = onGlobalPatches((target, patches) => { initializationGlobalPatches.push({ target, patches }); }); try { const boundObject2 = yjsBindingContext.apply(() => fromSnapshot(mobxKeystoneType, yjsJson), bindingContext); yjsBindingContext.set(boundObject2, { ...bindingContext, boundObject: boundObject2 }); return boundObject2; } finally { disposeOnGlobalPatches(); } }; const boundObject = createBoundObject(); const observeDeepCb = action((events) => { const patches = []; events.forEach((event) => { var _a; if (event.transaction.origin !== yjsOrigin) { patches.push(...convertYjsEventToPatches(event)); } if (event.target instanceof Y.Map || event.target instanceof Y.Array) { (_a = getYjsCollectionAtom(event.target)) == null ? void 0 : _a.reportChanged(); } }); if (patches.length > 0) { applyingYjsChangesToMobxKeystone++; try { applyPatches(boundObject, patches); } finally { applyingYjsChangesToMobxKeystone--; } } }); yjsObject.observeDeep(observeDeepCb); let pendingArrayOfArrayOfPatches = []; const disposeOnPatches = onPatches(boundObject, (patches) => { if (applyingYjsChangesToMobxKeystone > 0) { return; } pendingArrayOfArrayOfPatches.push(patches); }); const disposeOnSnapshot = onSnapshot(boundObject, () => { if (pendingArrayOfArrayOfPatches.length === 0) { return; } const arrayOfArrayOfPatches = pendingArrayOfArrayOfPatches; pendingArrayOfArrayOfPatches = []; yjsDoc.transact(() => { arrayOfArrayOfPatches.forEach((arrayOfPatches) => { arrayOfPatches.forEach((patch) => { applyMobxKeystonePatchToYjsObject(patch, yjsObject); }); }); }, yjsOrigin); }); yjsDoc.transact(() => { let boundObjectFound = false; initializationGlobalPatches.forEach(({ target, patches }) => { if (!boundObjectFound) { if (target !== boundObject) { return; } boundObjectFound = true; } const parentToChildPath = getParentToChildPath(boundObject, target); if (parentToChildPath !== void 0) { patches.forEach((patch) => { applyMobxKeystonePatchToYjsObject({ ...patch, path: [...parentToChildPath, ...patch.path] }, yjsObject); }); } }); }, yjsOrigin); return { boundObject, dispose: () => { disposeOnPatches(); disposeOnSnapshot(); yjsObject.unobserveDeep(observeDeepCb); }, yjsOrigin }; } export { MobxKeystoneYjsError, YjsTextModel, applyJsonArrayToYArray, applyJsonObjectToYMap, bindYjsToMobxKeystone, convertJsonToYjsData, yjsBindingContext, yjsTextModelId }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9ieC1rZXlzdG9uZS15anMuZXNtLm1qcyIsInNvdXJjZXMiOlsiLi4vc3JjL3V0aWxzL2Vycm9yLnRzIiwiLi4vc3JjL2JpbmRpbmcveWpzQmluZGluZ0NvbnRleHQudHMiLCIuLi9zcmMvdXRpbHMvZ2V0T3JDcmVhdGVZanNDb2xsZWN0aW9uQXRvbS50cyIsIi4uL3NyYy9iaW5kaW5nL3Jlc29sdmVZanNQYXRoLnRzIiwiLi4vc3JjL2JpbmRpbmcvWWpzVGV4dE1vZGVsLnRzIiwiLi4vc3JjL2JpbmRpbmcvY29udmVydEpzb25Ub1lqc0RhdGEudHMiLCIuLi9zcmMvYmluZGluZy9hcHBseU1vYnhLZXlzdG9uZVBhdGNoVG9ZanNPYmplY3QudHMiLCIuLi9zcmMvYmluZGluZy9jb252ZXJ0WWpzRGF0YVRvSnNvbi50cyIsIi4uL3NyYy9iaW5kaW5nL2NvbnZlcnRZanNFdmVudFRvUGF0Y2hlcy50cyIsIi4uL3NyYy9iaW5kaW5nL2JpbmRZanNUb01vYnhLZXlzdG9uZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcclxuICogQSBtb2J4LWtleXN0b25lLXlqcyBlcnJvci5cclxuICovXHJcbmV4cG9ydCBjbGFzcyBNb2J4S2V5c3RvbmVZanNFcnJvciBleHRlbmRzIEVycm9yIHtcclxuICBjb25zdHJ1Y3Rvcihtc2c6IHN0cmluZykge1xyXG4gICAgc3VwZXIobXNnKVxyXG5cclxuICAgIC8vIFNldCB0aGUgcHJvdG90eXBlIGV4cGxpY2l0bHkuXHJcbiAgICBPYmplY3Quc2V0UHJvdG90eXBlT2YodGhpcywgTW9ieEtleXN0b25lWWpzRXJyb3IucHJvdG90eXBlKVxyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEBpbnRlcm5hbFxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGZhaWx1cmUobXNnOiBzdHJpbmcpIHtcclxuICByZXR1cm4gbmV3IE1vYnhLZXlzdG9uZVlqc0Vycm9yKG1zZylcclxufVxyXG4iLCJpbXBvcnQgeyBBbnlUeXBlLCBjcmVhdGVDb250ZXh0IH0gZnJvbSBcIm1vYngta2V5c3RvbmVcIlxuaW1wb3J0ICogYXMgWSBmcm9tIFwieWpzXCJcblxuLyoqXG4gKiBDb250ZXh0IHdpdGggaW5mbyBvbiBob3cgYSBtb2J4LWtleXN0b25lIG1vZGVsIGlzIGJvdW5kIHRvIGEgWS5qcyBkYXRhIHN0cnVjdHVyZS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBZanNCaW5kaW5nQ29udGV4dCB7XG4gIC8qKlxuICAgKiBUaGUgWS5qcyBkb2N1bWVudC5cbiAgICovXG4gIHlqc0RvYzogWS5Eb2NcblxuICAvKipcbiAgICogVGhlIGJvdW5kIFkuanMgZGF0YSBzdHJ1Y3R1cmUuXG4gICAqL1xuICB5anNPYmplY3Q6IFkuTWFwPHVua25vd24+IHwgWS5BcnJheTx1bmtub3duPiB8IFkuVGV4dFxuXG4gIC8qKlxuICAgKiBUaGUgbW9ieC1rZXlzdG9uZSBtb2RlbCB0eXBlLlxuICAgKi9cbiAgbW9ieEtleXN0b25lVHlwZTogQW55VHlwZVxuXG4gIC8qKlxuICAgKiBUaGUgb3JpZ2luIHN5bWJvbCB1c2VkIGZvciB0cmFuc2FjdGlvbnMuXG4gICAqL1xuICB5anNPcmlnaW46IHN5bWJvbFxuXG4gIC8qKlxuICAgKiBUaGUgYm91bmQgbW9ieC1rZXlzdG9uZSBpbnN0YW5jZS5cbiAgICovXG4gIGJvdW5kT2JqZWN0OiB1bmtub3duXG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgd2UgYXJlIGN1cnJlbnRseSBhcHBseWluZyBZLmpzIGNoYW5nZXMgdG8gdGhlIG1vYngta2V5c3RvbmUgbW9kZWwuXG4gICAqL1xuICBpc0FwcGx5aW5nWWpzQ2hhbmdlc1RvTW9ieEtleXN0b25lOiBib29sZWFuXG59XG5cbi8qKlxuICogQ29udGV4dCB3aXRoIGluZm8gb24gaG93IGEgbW9ieC1rZXlzdG9uZSBtb2RlbCBpcyBib3VuZCB0byBhIFkuanMgZGF0YSBzdHJ1Y3R1cmUuXG4gKi9cbmV4cG9ydCBjb25zdCB5anNCaW5kaW5nQ29udGV4dCA9IGNyZWF0ZUNvbnRleHQ8WWpzQmluZGluZ0NvbnRleHQgfCB1bmRlZmluZWQ+KHVuZGVmaW5lZClcbiIsImltcG9ydCB7IElBdG9tLCBjcmVhdGVBdG9tIH0gZnJvbSBcIm1vYnhcIlxuaW1wb3J0ICogYXMgWSBmcm9tIFwieWpzXCJcblxuY29uc3QgeWpzQ29sbGVjdGlvbkF0b21zID0gbmV3IFdlYWtNYXA8WS5NYXA8dW5rbm93bj4gfCBZLkFycmF5PHVua25vd24+LCBJQXRvbT4oKVxuXG4vKipcbiAqIEBpbnRlcm5hbFxuICovXG5leHBvcnQgY29uc3QgZ2V0WWpzQ29sbGVjdGlvbkF0b20gPSAoXG4gIHlqc0NvbGxlY3Rpb246IFkuTWFwPHVua25vd24+IHwgWS5BcnJheTx1bmtub3duPlxuKTogSUF0b20gfCB1bmRlZmluZWQgPT4ge1xuICByZXR1cm4geWpzQ29sbGVjdGlvbkF0b21zLmdldCh5anNDb2xsZWN0aW9uKVxufVxuXG4vKipcbiAqIEBpbnRlcm5hbFxuICovXG5leHBvcnQgY29uc3QgZ2V0T3JDcmVhdGVZanNDb2xsZWN0aW9uQXRvbSA9IChcbiAgeWpzQ29sbGVjdGlvbjogWS5NYXA8dW5rbm93bj4gfCBZLkFycmF5PHVua25vd24+XG4pOiBJQXRvbSA9PiB7XG4gIGxldCBhdG9tID0geWpzQ29sbGVjdGlvbkF0b21zLmdldCh5anNDb2xsZWN0aW9uKVxuICBpZiAoIWF0b20pIHtcbiAgICBhdG9tID0gY3JlYXRlQXRvbShgeWpzQ29sbGVjdGlvbkF0b21gKVxuICAgIHlqc0NvbGxlY3Rpb25BdG9tcy5zZXQoeWpzQ29sbGVjdGlvbiwgYXRvbSlcbiAgfVxuICByZXR1cm4gYXRvbVxufVxuIiwiaW1wb3J0ICogYXMgWSBmcm9tIFwieWpzXCJcbmltcG9ydCB7IGZhaWx1cmUgfSBmcm9tIFwiLi4vdXRpbHMvZXJyb3JcIlxuaW1wb3J0IHsgZ2V0T3JDcmVhdGVZanNDb2xsZWN0aW9uQXRvbSB9IGZyb20gXCIuLi91dGlscy9nZXRPckNyZWF0ZVlqc0NvbGxlY3Rpb25BdG9tXCJcblxuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVZanNQYXRoKHlqc09iamVjdDogdW5rbm93biwgcGF0aDogcmVhZG9ubHkgKHN0cmluZyB8IG51bWJlcilbXSk6IHVua25vd24ge1xuICBsZXQgY3VycmVudFlqc09iamVjdDogdW5rbm93biA9IHlqc09iamVjdFxuXG4gIHBhdGguZm9yRWFjaCgocGF0aFBhcnQsIGkpID0+IHtcbiAgICBpZiAoY3VycmVudFlqc09iamVjdCBpbnN0YW5jZW9mIFkuTWFwKSB7XG4gICAgICBnZXRPckNyZWF0ZVlqc0NvbGxlY3Rpb25BdG9tKGN1cnJlbnRZanNPYmplY3QpLnJlcG9ydE9ic2VydmVkKClcbiAgICAgIGNvbnN0IGtleSA9IFN0cmluZyhwYXRoUGFydClcbiAgICAgIGN1cnJlbnRZanNPYmplY3QgPSBjdXJyZW50WWpzT2JqZWN0LmdldChrZXkpXG4gICAgfSBlbHNlIGlmIChjdXJyZW50WWpzT2JqZWN0IGluc3RhbmNlb2YgWS5BcnJheSkge1xuICAgICAgZ2V0T3JDcmVhdGVZanNDb2xsZWN0aW9uQXRvbShjdXJyZW50WWpzT2JqZWN0KS5yZXBvcnRPYnNlcnZlZCgpXG4gICAgICBjb25zdCBrZXkgPSBOdW1iZXIocGF0aFBhcnQpXG4gICAgICBjdXJyZW50WWpzT2JqZWN0ID0gY3VycmVudFlqc09iamVjdC5nZXQoa2V5KVxuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBmYWlsdXJlKFxuICAgICAgICBgWS5NYXAgb3IgWS5BcnJheSB3YXMgZXhwZWN0ZWQgYXQgcGF0aCAke0pTT04uc3RyaW5naWZ5KFxuICAgICAgICAgIHBhdGguc2xpY2UoMCwgaSlcbiAgICAgICAgKX0gaW4gb3JkZXIgdG8gcmVzb2x2ZSBwYXRoICR7SlNPTi5zdHJpbmdpZnkocGF0aCl9LCBidXQgZ290ICR7Y3VycmVudFlqc09iamVjdH0gaW5zdGVhZGBcbiAgICAgIClcbiAgICB9XG4gIH0pXG5cbiAgcmV0dXJuIGN1cnJlbnRZanNPYmplY3Rcbn1cbiIsImltcG9ydCB7IElBdG9tLCBjb21wdXRlZCwgY3JlYXRlQXRvbSwgb2JzZXJ2ZSwgcmVhY3Rpb24gfSBmcm9tIFwibW9ieFwiXHJcbmltcG9ydCB7XHJcbiAgRnJvemVuLFxyXG4gIE1vZGVsLFxyXG4gIGZyb3plbixcclxuICBnZXRQYXJlbnRUb0NoaWxkUGF0aCxcclxuICBtb2RlbCxcclxuICBvblNuYXBzaG90LFxyXG4gIHRQcm9wLFxyXG4gIHR5cGVzLFxyXG59IGZyb20gXCJtb2J4LWtleXN0b25lXCJcclxuaW1wb3J0ICogYXMgWSBmcm9tIFwieWpzXCJcclxuaW1wb3J0IHsgZmFpbHVyZSB9IGZyb20gXCIuLi91dGlscy9lcnJvclwiXHJcbmltcG9ydCB7IFlqc0JpbmRpbmdDb250ZXh0LCB5anNCaW5kaW5nQ29udGV4dCB9IGZyb20gXCIuL3lqc0JpbmRpbmdDb250ZXh0XCJcclxuaW1wb3J0IHsgcmVzb2x2ZVlqc1BhdGggfSBmcm9tIFwiLi9yZXNvbHZlWWpzUGF0aFwiXHJcblxyXG4vLyBEZWx0YVtdW10sIHNpbmNlIGVhY2ggc2luZ2xlIGNoYW5nZSBpcyBhIERlbHRhW11cclxuLy8gd2UgdXNlIGZyb3plbiBzbyB0aGF0IHdlIGNhbiByZXVzZSBlYWNoIGRlbHRhIGNoYW5nZVxyXG5jb25zdCBkZWx0YUxpc3RUeXBlID0gdHlwZXMuYXJyYXkodHlwZXMuZnJvemVuKHR5cGVzLnVuY2hlY2tlZDx1bmtub3duW10+KCkpKVxyXG5cclxuZXhwb3J0IGNvbnN0IHlqc1RleHRNb2RlbElkID0gXCJtb2J4LWtleXN0b25lLXlqcy9ZanNUZXh0TW9kZWxcIlxyXG5cclxuLyoqXHJcbiAqIEEgbW9ieC1rZXlzdG9uZSBtb2RlbCB0aGF0IHJlcHJlc2VudHMgYSBZanMuVGV4dCBvYmplY3QuXHJcbiAqL1xyXG5AbW9kZWwoeWpzVGV4dE1vZGVsSWQpXHJcbmV4cG9ydCBjbGFzcyBZanNUZXh0TW9kZWwgZXh0ZW5kcyBNb2RlbCh7XHJcbiAgZGVsdGFMaXN0OiB0UHJvcChkZWx0YUxpc3RUeXBlLCAoKSA9PiBbXSksXHJcbn0pIHtcclxuICAvKipcclxuICAgKiBIZWxwZXIgZnVuY3Rpb24gdG8gY3JlYXRlIGEgWWpzVGV4dE1vZGVsIGluc3RhbmNlIHdpdGggYSBzaW1wbGUgdGV4dC5cclxuICAgKi9cclxuICBzdGF0aWMgd2l0aFRleHQodGV4dDogc3RyaW5nKTogWWpzVGV4dE1vZGVsIHtcclxuICAgIHJldHVybiBuZXcgRGVjb3JhdGVkWWpzVGV4dE1vZGVsKHtcclxuICAgICAgZGVsdGFMaXN0OiBbXHJcbiAgICAgICAgZnJvemVuKFtcclxuICAgICAgICAgIHtcclxuICAgICAgICAgICAgaW5zZXJ0OiB0ZXh0LFxyXG4gICAgICAgICAgfSxcclxuICAgICAgICBdKSxcclxuICAgICAgXSxcclxuICAgIH0pXHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBUaGUgWS5qcyBwYXRoIGZyb20gdGhlIGJvdW5kIG9iamVjdCB0byB0aGUgWWpzVGV4dE1vZGVsIGluc3RhbmNlLlxyXG4gICAqL1xyXG4gIEBjb21wdXRlZFxyXG4gIHByaXZhdGUgZ2V0IF95anNPYmplY3RQYXRoKCkge1xyXG4gICAgY29uc3QgY3R4ID0geWpzQmluZGluZ0NvbnRleHQuZ2V0KHRoaXMpXHJcbiAgICBpZiAoY3R4Py5ib3VuZE9iamVjdCA9PSBudWxsKSB7XHJcbiAgICAgIHRocm93IGZhaWx1cmUoXHJcbiAgICAgICAgXCJ0aGUgWWpzVGV4dE1vZGVsIGluc3RhbmNlIG11c3QgYmUgcGFydCBvZiBhIGJvdW5kIG9iamVjdCBiZWZvcmUgaXQgY2FuIGJlIGFjY2Vzc2VkXCJcclxuICAgICAgKVxyXG4gICAgfVxyXG5cclxuICAgIGNvbnN0IHBhdGggPSBnZXRQYXJlbnRUb0NoaWxkUGF0aChjdHguYm91bmRPYmplY3QsIHRoaXMpXHJcbiAgICBpZiAoIXBhdGgpIHtcclxuICAgICAgdGhyb3cgZmFpbHVyZShcImEgcGF0aCBmcm9tIHRoZSBib3VuZCBvYmplY3QgdG8gdGhlIFlqc1RleHRNb2RlbCBpbnN0YW5jZSBpcyBub3QgYXZhaWxhYmxlXCIpXHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIHBhdGhcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFRoZSBZanMuVGV4dCBvYmplY3QgcHJlc2VudCBhdCB0aGlzIG1vYngta2V5c3RvbmUgbm9kZSdzIHBhdGguXHJcbiAgICovXHJcbiAgQGNvbXB1dGVkXHJcbiAgcHJpdmF0ZSBnZXQgX3lqc09iamVjdEF0UGF0aCgpOiB1bmtub3duIHtcclxuICAgIGNvbnN0IHBhdGggPSB0aGlzLl95anNPYmplY3RQYXRoXHJcblxyXG4gICAgY29uc3QgY3R4ID0geWpzQmluZGluZ0NvbnRleHQuZ2V0KHRoaXMpIVxyXG5cclxuICAgIHJldHVybiByZXNvbHZlWWpzUGF0aChjdHgueWpzT2JqZWN0LCBwYXRoKVxyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogVGhlIFlqcy5UZXh0IG9iamVjdCByZXByZXNlbnRlZCBieSB0aGlzIG1vYngta2V5c3RvbmUgbm9kZS5cclxuICAgKi9cclxuICBAY29tcHV0ZWRcclxuICBnZXQgeWpzVGV4dCgpOiBZLlRleHQge1xyXG4gICAgY29uc3QgeWpzT2JqZWN0ID0gdGhpcy5feWpzT2JqZWN0QXRQYXRoXHJcblxyXG4gICAgaWYgKCEoeWpzT2JqZWN0IGluc3RhbmNlb2YgWS5UZXh0KSkge1xyXG4gICAgICB0aHJvdyBmYWlsdXJlKGBZLlRleHQgd2FzIGV4cGVjdGVkIGF0IHBhdGggJHtKU09OLnN0cmluZ2lmeSh0aGlzLl95anNPYmplY3RQYXRoKX1gKVxyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiB5anNPYmplY3RcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEF0b20gdGhhdCBnZXRzIGNoYW5nZWQgd2hlbiB0aGUgYXNzb2NpYXRlZCBZLmpzIHRleHQgY2hhbmdlcy5cclxuICAgKi9cclxuICB5anNUZXh0Q2hhbmdlZEF0b20gPSBjcmVhdGVBdG9tKFwieWpzVGV4dENoYW5nZWRBdG9tXCIpXHJcblxyXG4gIC8qKlxyXG4gICAqIFRoZSB0ZXh0IHZhbHVlIG9mIHRoZSBZanMuVGV4dCBvYmplY3QuXHJcbiAgICogU2hvcnRjdXQgZm9yIGB5anNUZXh0LnRvU3RyaW5nKClgLCBidXQgY29tcHV0ZWQuXHJcbiAgICovXHJcbiAgQGNvbXB1dGVkXHJcbiAgZ2V0IHRleHQoKTogc3RyaW5nIHtcclxuICAgIHRoaXMueWpzVGV4dENoYW5nZWRBdG9tLnJlcG9ydE9ic2VydmVkKClcclxuICAgIHJldHVybiB0aGlzLnlqc1RleHQudG9TdHJpbmcoKVxyXG4gIH1cclxuXHJcbiAgcHJvdGVjdGVkIG9uSW5pdCgpIHtcclxuICAgIGNvbnN0IHNob3VsZFJlcGxpY2F0ZVRvWWpzID0gKGN0eDogWWpzQmluZGluZ0NvbnRleHQgfCB1bmRlZmluZWQpOiBjdHggaXMgWWpzQmluZGluZ0NvbnRleHQgPT4ge1xyXG4gICAgICByZXR1cm4gISFjdHggJiYgISFjdHguYm91bmRPYmplY3QgJiYgIWN0eC5pc0FwcGx5aW5nWWpzQ2hhbmdlc1RvTW9ieEtleXN0b25lXHJcbiAgICB9XHJcblxyXG4gICAgbGV0IHJlYXBwbHlEZWx0YXNUb1lqc1RleHQgPSBmYWxzZVxyXG4gICAgY29uc3QgbmV3RGVsdGFzOiBGcm96ZW48dW5rbm93bltdPltdID0gW11cclxuXHJcbiAgICBsZXQgZGlzcG9zZU9ic2VydmVEZWx0YUxpc3Q6ICgoKSA9PiB2b2lkKSB8IHVuZGVmaW5lZFxyXG5cclxuICAgIGNvbnN0IGRpc3Bvc2VSZWFjdGlvblRvRGVsdGFMaXN0UmVmQ2hhbmdlID0gcmVhY3Rpb24oXHJcbiAgICAgICgpID0+IHRoaXMuJC5kZWx0YUxpc3QsXHJcbiAgICAgIChkZWx0YUxpc3QpID0+IHtcclxuICAgICAgICBkaXNwb3NlT2JzZXJ2ZURlbHRhTGlzdD8uKClcclxuICAgICAgICBkaXNwb3NlT2JzZXJ2ZURlbHRhTGlzdCA9IHVuZGVmaW5lZFxyXG5cclxuICAgICAgICBkaXNwb3NlT2JzZXJ2ZURlbHRhTGlzdCA9IG9ic2VydmUoZGVsdGFMaXN0LCAoY2hhbmdlKSA9PiB7XHJcbiAgICAgICAgICBpZiAocmVhcHBseURlbHRhc1RvWWpzVGV4dCkge1xyXG4gICAgICAgICAgICAvLyBhbHJlYWR5IGdvbm5hIHJlcGxhY2UgdGhlbSBhbGxcclxuICAgICAgICAgICAgcmV0dXJuXHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICBpZiAoIXNob3VsZFJlcGxpY2F0ZVRvWWpzKHlqc0JpbmRpbmdDb250ZXh0LmdldCh0aGlzKSkpIHtcclxuICAgICAgICAgICAgLy8geWpzIHRleHQgaXMgYWxyZWFkeSB1cCB0byBkYXRlIHdpdGggdGhlc2UgY2hhbmdlc1xyXG4gICAgICAgICAgICByZXR1cm5cclxuICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICBpZiAoXHJcbiAgICAgICAgICAgIGNoYW5nZS50eXBlID09PSBcInNwbGljZVwiICYmXHJcbiAgICAgICAgICAgIGNoYW5nZS5yZW1vdmVkQ291bnQgPT09IDAgJiZcclxuICAgICAgICAgICAgY2hhbmdlLmFkZGVkQ291bnQgPiAwICYmXHJcbiAgICAgICAgICAgIGNoYW5nZS5pbmRleCA9PT0gdGhpcy5kZWx0YUxpc3QubGVuZ3RoXHJcbiAgICAgICAgICApIHtcclxuICAgICAgICAgICAgLy8gb3B0aW1pemF0aW9uLCBqdXN0IGFkZGluZyBuZXcgb25lcyB0byB0aGUgZW5kXHJcbiAgICAgICAgICAgIG5ld0RlbHRhcy5wdXNoKC4uLmNoYW5nZS5hZGRlZClcclxuICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIC8vIGFueSBvdGhlciBjaGFuZ2UsIHdlIG5lZWQgdG8gcmVhcHBseSBhbGwgZGVsdGFzXHJcbiAgICAgICAgICAgIHJlYXBwbHlEZWx0YXNUb1lqc1RleHQgPSB0cnVlXHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSlcclxuICAgICAgfSxcclxuICAgICAgeyBmaXJlSW1tZWRpYXRlbHk6IHRydWUgfVxyXG4gICAgKVxyXG5cclxuICAgIGNvbnN0IGRpc3Bvc2VPblNuYXBzaG90ID0gb25TbmFwc2hvdCh0aGlzLCAoKSA9PiB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgaWYgKHJlYXBwbHlEZWx0YXNUb1lqc1RleHQpIHtcclxuICAgICAgICAgIGNvbnN0IGN0eCA9IHlqc0JpbmRpbmdDb250ZXh0LmdldCh0aGlzKVxyXG5cclxuICAgICAgICAgIGlmIChzaG91bGRSZXBsaWNhdGVUb1lqcyhjdHgpKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IHsgeWpzVGV4dCB9ID0gdGhpc1xyXG5cclxuICAgICAgICAgICAgY3R4Lnlqc0RvYy50cmFuc2FjdCgoKSA9PiB7XHJcbiAgICAgICAgICAgICAgLy8gZGlkbid0IGZpbmQgYSBiZXR0ZXIgd2F5IHRoYW4gdGhpcyB0byByZWFwcGx5IGFsbCBkZWx0YXNcclxuICAgICAgICAgICAgICAvLyB3aXRob3V0IGhhdmluZyB0byByZS1jcmVhdGUgdGhlIFkuVGV4dCBvYmplY3RcclxuICAgICAgICAgICAgICBpZiAoeWpzVGV4dC5sZW5ndGggPiAwKSB7XHJcbiAgICAgICAgICAgICAgICB5anNUZXh0LmRlbGV0ZSgwLCB5anNUZXh0Lmxlbmd0aClcclxuICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgIHRoaXMuZGVsdGFMaXN0LmZvckVhY2goKGZyb3plbkRlbHRhcykgPT4ge1xyXG4gICAgICAgICAgICAgICAgeWpzVGV4dC5hcHBseURlbHRhKGZyb3plbkRlbHRhcy5kYXRhKVxyXG4gICAgICAgICAgICAgIH0pXHJcbiAgICAgICAgICAgIH0sIGN0eC55anNPcmlnaW4pXHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSBlbHNlIGlmIChuZXdEZWx0YXMubGVuZ3RoID4gMCkge1xyXG4gICAgICAgICAgY29uc3QgY3R4ID0geWpzQmluZGluZ0NvbnRleHQuZ2V0KHRoaXMpXHJcblxyXG4gICAgICAgICAgaWYgKHNob3VsZFJlcGxpY2F0ZVRvWWpzKGN0eCkpIHtcclxuICAgICAgICAgICAgY29uc3QgeyB5anNUZXh0IH0gPSB0aGlzXHJcblxyXG4gICAgICAgICAgICBjdHgueWpzRG9jLnRyYW5zYWN0KCgpID0+IHtcclxuICAgICAgICAgICAgICBuZXdEZWx0YXMuZm9yRWFjaCgoZnJvemVuRGVsdGFzKSA9PiB7XHJcbiAgICAgICAgICAgICAgICB5anNUZXh0LmFwcGx5RGVsdGEoZnJvemVuRGVsdGFzLmRhdGEpXHJcbiAgICAgICAgICAgICAgfSlcclxuICAgICAgICAgICAgfSwgY3R4Lnlqc09yaWdpbilcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH0gZmluYWxseSB7XHJcbiAgICAgICAgcmVhcHBseURlbHRhc1RvWWpzVGV4dCA9IGZhbHNlXHJcbiAgICAgICAgbmV3RGVsdGFzLmxlbmd0aCA9IDBcclxuICAgICAgfVxyXG4gICAgfSlcclxuXHJcbiAgICBjb25zdCBkaXBvc2VZanNUZXh0Q2hhbmdlZEF0b20gPSBob29rWWpzVGV4dENoYW5nZWRBdG9tKFxyXG4gICAgICAoKSA9PiB0aGlzLnlqc1RleHQsXHJcbiAgICAgIHRoaXMueWpzVGV4dENoYW5nZWRBdG9tXHJcbiAgICApXHJcblxyXG4gICAgcmV0dXJuICgpID0+IHtcclxuICAgICAgZGlzcG9zZU9uU25hcHNob3QoKVxyXG4gICAgICBkaXNwb3NlUmVhY3Rpb25Ub0RlbHRhTGlzdFJlZkNoYW5nZSgpXHJcbiAgICAgIGRpc3Bvc2VPYnNlcnZlRGVsdGFMaXN0Py4oKVxyXG4gICAgICBkaXNwb3NlT2JzZXJ2ZURlbHRhTGlzdCA9IHVuZGVmaW5lZFxyXG5cclxuICAgICAgZGlwb3NlWWpzVGV4dENoYW5nZWRBdG9tKClcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbi8vIHdlIHVzZSB0aGlzIHRyaWNrIGp1c3QgdG8gYXZvaWQgYSBiYWJlbCBidWcgdGhhdCBjYXVzZXMgY2xhc3NlcyB1c2VkIGluc2lkZSBjbGFzc2VzIG5vdCB0byBiZSBvdmVycmlkZW5cclxuLy8gYnkgdGhlIGRlY29yYXRvclxyXG5jb25zdCBEZWNvcmF0ZWRZanNUZXh0TW9kZWwgPSBZanNUZXh0TW9kZWxcclxuXHJcbmZ1bmN0aW9uIGhvb2tZanNUZXh0Q2hhbmdlZEF0b20oZ2V0WWpzVGV4dDogKCkgPT4gWS5UZXh0LCB0ZXh0Q2hhbmdlZEF0b206IElBdG9tKSB7XHJcbiAgbGV0IGRpc3Bvc2VPYnNlcnZlWWpzVGV4dDogKCgpID0+IHZvaWQpIHwgdW5kZWZpbmVkXHJcblxyXG4gIGNvbnN0IG9ic2VydmVGbiA9ICgpID0+IHtcclxuICAgIHRleHRDaGFuZ2VkQXRvbS5yZXBvcnRDaGFuZ2VkKClcclxuICB9XHJcblxyXG4gIGNvbnN0IGRpc3Bvc2VSZWFjdGlvblRvWVRleHRDaGFuZ2UgPSByZWFjdGlvbihcclxuICAgICgpID0+IHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICByZXR1cm4gZ2V0WWpzVGV4dCgpXHJcbiAgICAgIH0gY2F0Y2gge1xyXG4gICAgICAgIHJldHVybiB1bmRlZmluZWRcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgICh5anNUZXh0KSA9PiB7XHJcbiAgICAgIGRpc3Bvc2VPYnNlcnZlWWpzVGV4dD8uKClcclxuICAgICAgZGlzcG9zZU9ic2VydmVZanNUZXh0ID0gdW5kZWZpbmVkXHJcblxyXG4gICAgICBpZiAoeWpzVGV4dCkge1xyXG4gICAgICAgIHlqc1RleHQub2JzZXJ2ZShvYnNlcnZlRm4pXHJcblxyXG4gICAgICAgIGRpc3Bvc2VPYnNlcnZlWWpzVGV4dCA9ICgpID0+IHtcclxuICAgICAgICAgIHlqc1RleHQudW5vYnNlcnZlKG9ic2VydmVGbilcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIHRleHRDaGFuZ2VkQXRvbS5yZXBvcnRDaGFuZ2VkKClcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIGZpcmVJbW1lZGlhdGVseTogdHJ1ZSxcclxuICAgIH1cclxuICApXHJcblxyXG4gIHJldHVybiAoKSA9PiB7XHJcbiAgICBkaXNwb3NlUmVhY3Rpb25Ub1lUZXh0Q2hhbmdlKClcclxuICAgIGRpc3Bvc2VPYnNlcnZlWWpzVGV4dD8uKClcclxuICAgIGRpc3Bvc2VPYnNlcnZlWWpzVGV4dCA9IHVuZGVmaW5lZFxyXG4gIH1cclxufVxyXG4iLCJpbXBvcnQgKiBhcyBZIGZyb20gXCJ5anNcIlxuaW1wb3J0IHsgWWpzVGV4dE1vZGVsLCB5anNUZXh0TW9kZWxJZCB9IGZyb20gXCIuL1lqc1RleHRNb2RlbFwiXG5pbXBvcnQgeyBTbmFwc2hvdE91dE9mIH0gZnJvbSBcIm1vYngta2V5c3RvbmVcIlxuaW1wb3J0IHsgWWpzRGF0YSB9IGZyb20gXCIuL2NvbnZlcnRZanNEYXRhVG9Kc29uXCJcbmltcG9ydCB7IFBsYWluQXJyYXksIFBsYWluT2JqZWN0LCBQbGFpblByaW1pdGl2ZSwgUGxhaW5WYWx1ZSB9IGZyb20gXCIuLi9wbGFpblR5cGVzXCJcbmltcG9ydCB7IGFjdGlvbiwgcnVuSW5BY3Rpb24gfSBmcm9tIFwibW9ieFwiXG5cbmZ1bmN0aW9uIGlzUGxhaW5QcmltaXRpdmUodjogUGxhaW5WYWx1ZSk6IHYgaXMgUGxhaW5QcmltaXRpdmUge1xuICBjb25zdCB0ID0gdHlwZW9mIHZcbiAgcmV0dXJuIHQgPT09IFwic3RyaW5nXCIgfHwgdCA9PT0gXCJudW1iZXJcIiB8fCB0ID09PSBcImJvb2xlYW5cIiB8fCB2ID09PSBudWxsIHx8IHYgPT09IHVuZGVmaW5lZFxufVxuXG5mdW5jdGlvbiBpc1BsYWluQXJyYXkodjogUGxhaW5WYWx1ZSk6IHYgaXMgUGxhaW5BcnJheSB7XG4gIHJldHVybiBBcnJheS5pc0FycmF5KHYpXG59XG5cbmZ1bmN0aW9uIGlzUGxhaW5PYmplY3QodjogUGxhaW5WYWx1ZSk6IHYgaXMgUGxhaW5PYmplY3Qge1xuICByZXR1cm4gIWlzUGxhaW5BcnJheSh2KSAmJiB0eXBlb2YgdiA9PT0gXCJvYmplY3RcIiAmJiB2ICE9PSBudWxsXG59XG5cbi8qKlxuICogQ29udmVydHMgYSBwbGFpbiB2YWx1ZSB0byBhIFkuanMgZGF0YSBzdHJ1Y3R1cmUuXG4gKiBPYmplY3RzIGFyZSBjb252ZXJ0ZWQgdG8gWS5NYXBzLCBhcnJheXMgdG8gWS5BcnJheXMsIHByaW1pdGl2ZXMgYXJlIHVudG91Y2hlZC5cbiAqIEZyb3plbiB2YWx1ZXMgYXJlIGEgc3BlY2lhbCBjYXNlIGFuZCB0aGV5IGFyZSBrZXB0IGFzIGltbXV0YWJsZSBwbGFpbiB2YWx1ZXMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb252ZXJ0SnNvblRvWWpzRGF0YSh2OiBQbGFpblZhbHVlKTogWWpzRGF0YSB7XG4gIHJldHVybiBydW5JbkFjdGlvbigoKSA9PiB7XG4gICAgaWYgKGlzUGxhaW5QcmltaXRpdmUodikpIHtcbiAgICAgIHJldHVybiB2XG4gICAgfVxuXG4gICAgaWYgKGlzUGxhaW5BcnJheSh2KSkge1xuICAgICAgY29uc3QgYXJyID0gbmV3IFkuQXJyYXkoKVxuICAgICAgYXBwbHlKc29uQXJyYXlUb1lBcnJheShhcnIsIHYpXG4gICAgICByZXR1cm4gYXJyXG4gICAgfVxuXG4gICAgaWYgKGlzUGxhaW5PYmplY3QodikpIHtcbiAgICAgIGlmICh2LiRmcm96ZW4gPT09IHRydWUpIHtcbiAgICAgICAgLy8gZnJvemVuIHZhbHVlLCBzYXZlIGFzIGltbXV0YWJsZSBvYmplY3RcbiAgICAgICAgcmV0dXJuIHZcbiAgICAgIH1cblxuICAgICAgaWYgKHYuJG1vZGVsVHlwZSA9PT0geWpzVGV4dE1vZGVsSWQpIHtcbiAgICAgICAgY29uc3QgdGV4dCA9IG5ldyBZLlRleHQoKVxuICAgICAgICBjb25zdCB5anNUZXh0TW9kZWwgPSB2IGFzIHVua25vd24gYXMgU25hcHNob3RPdXRPZjxZanNUZXh0TW9kZWw+XG4gICAgICAgIHlqc1RleHRNb2RlbC5kZWx0YUxpc3QuZm9yRWFjaCgoZnJvemVuRGVsdGFzKSA9PiB7XG4gICAgICAgICAgdGV4dC5hcHBseURlbHRhKGZyb3plbkRlbHRhcy5kYXRhKVxuICAgICAgICB9KVxuICAgICAgICByZXR1cm4gdGV4dFxuICAgICAgfVxuXG4gICAgICBjb25zdCBtYXAgPSBuZXcgWS5NYXAoKVxuICAgICAgYXBwbHlKc29uT2JqZWN0VG9ZTWFwKG1hcCwgdilcbiAgICAgIHJldHVybiBtYXBcbiAgICB9XG5cbiAgICB0aHJvdyBuZXcgRXJyb3IoYHVuc3VwcG9ydGVkIHZhbHVlIHR5cGU6ICR7dn1gKVxuICB9KVxufVxuXG4vKipcbiAqIEFwcGxpZXMgYSBKU09OIGFycmF5IHRvIGEgWS5BcnJheSwgdXNpbmcgdGhlIGNvbnZlcnRKc29uVG9ZanNEYXRhIHRvIGNvbnZlcnQgdGhlIHZhbHVlcy5cbiAqL1xuZXhwb3J0IGNvbnN0IGFwcGx5SnNvbkFycmF5VG9ZQXJyYXkgPSBhY3Rpb24oKGRlc3Q6IFkuQXJyYXk8YW55Piwgc291cmNlOiBQbGFpbkFycmF5KSA9PiB7XG4gIGRlc3QucHVzaChzb3VyY2UubWFwKGNvbnZlcnRKc29uVG9ZanNEYXRhKSlcbn0pXG5cbi8qKlxuICogQXBwbGllcyBhIEpTT04gb2JqZWN0IHRvIGEgWS5NYXAsIHVzaW5nIHRoZSBjb252ZXJ0SnNvblRvWWpzRGF0YSB0byBjb252ZXJ0IHRoZSB2YWx1ZXMuXG4gKi9cbmV4cG9ydCBjb25zdCBhcHBseUpzb25PYmplY3RUb1lNYXAgPSBhY3Rpb24oKGRlc3Q6IFkuTWFwPGFueT4sIHNvdXJjZTogUGxhaW5PYmplY3QpID0+IHtcbiAgT2JqZWN0LmVudHJpZXMoc291cmNlKS5mb3JFYWNoKChbaywgdl0pID0+IHtcbiAgICBkZXN0LnNldChrLCBjb252ZXJ0SnNvblRvWWpzRGF0YSh2KSlcbiAgfSlcbn0pXG4iLCJpbXBvcnQgeyBQYXRjaCB9IGZyb20gXCJtb2J4LWtleXN0b25lXCJcbmltcG9ydCAqIGFzIFkgZnJvbSBcInlqc1wiXG5pbXBvcnQgeyBmYWlsdXJlIH0gZnJvbSBcIi4uL3V0aWxzL2Vycm9yXCJcbmltcG9ydCB7IGNvbnZlcnRKc29uVG9ZanNEYXRhIH0gZnJvbSBcIi4vY29udmVydEpzb25Ub1lqc0RhdGFcIlxuaW1wb3J0IHsgUGxhaW5WYWx1ZSB9IGZyb20gXCIuLi9wbGFpblR5cGVzXCJcblxuZXhwb3J0IGZ1bmN0aW9uIGFwcGx5TW9ieEtleXN0b25lUGF0Y2hUb1lqc09iamVjdChwYXRjaDogUGF0Y2gsIHlqczogdW5rbm93bik6IHZvaWQge1xuICBpZiAocGF0Y2gucGF0aC5sZW5ndGggPiAxKSB7XG4gICAgY29uc3QgW2tleSwgLi4ucmVzdF0gPSBwYXRjaC5wYXRoXG5cbiAgICBpZiAoeWpzIGluc3RhbmNlb2YgWS5NYXApIHtcbiAgICAgIGNvbnN0IGNoaWxkID0geWpzLmdldChTdHJpbmcoa2V5KSkgYXMgdW5rbm93blxuICAgICAgaWYgKGNoaWxkID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgdGhyb3cgZmFpbHVyZShcbiAgICAgICAgICBgaW52YWxpZCBwYXRjaCBwYXRoLCBrZXkgXCIke2tleX1cIiBub3QgZm91bmQgaW4gWWpzIG1hcCAtIHBhdGNoOiAke0pTT04uc3RyaW5naWZ5KHBhdGNoKX1gXG4gICAgICAgIClcbiAgICAgIH1cbiAgICAgIGFwcGx5TW9ieEtleXN0b25lUGF0Y2hUb1lqc09iamVjdCh7IC4uLnBhdGNoLCBwYXRoOiByZXN0IH0sIGNoaWxkKVxuICAgIH0gZWxzZSBpZiAoeWpzIGluc3RhbmNlb2YgWS5BcnJheSkge1xuICAgICAgY29uc3QgY2hpbGQgPSB5anMuZ2V0KE51bWJlcihrZXkpKSBhcyB1bmtub3duXG4gICAgICBpZiAoY2hpbGQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICB0aHJvdyBmYWlsdXJlKFxuICAgICAgICAgIGBpbnZhbGlkIHBhdGNoIHBhdGgsIGtleSBcIiR7a2V5fVwiIG5vdCBmb3VuZCBpbiBZanMgYXJyYXkgLSBwYXRjaDogJHtKU09OLnN0cmluZ2lmeShcbiAgICAgICAgICAgIHBhdGNoXG4gICAgICAgICAgKX1gXG4gICAgICAgIClcbiAgICAgIH1cbiAgICAgIGFwcGx5TW9ieEtleXN0b25lUGF0Y2hUb1lqc09iamVjdCh7IC4uLnBhdGNoLCBwYXRoOiByZXN0IH0sIGNoaWxkKVxuICAgIH0gZWxzZSBpZiAoeWpzIGluc3RhbmNlb2YgWS5UZXh0KSB7XG4gICAgICAvLyBjaGFuZ2VzIHRvIGRlbHRhTGlzdCB3aWxsIGJlIGhhbmRsZWQgYnkgdGhlIGFycmF5IG9ic2VydmUgaW4gdGhlIFlqc1RleHRNb2RlbCBjbGFzc1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBmYWlsdXJlKFxuICAgICAgICBgaW52YWxpZCBwYXRjaCBwYXRoLCBrZXkgXCIke2tleX1cIiBub3QgZm91bmQgaW4gdW5rbm93biBZanMgb2JqZWN0IC0gcGF0Y2g6ICR7SlNPTi5zdHJpbmdpZnkoXG4gICAgICAgICAgcGF0Y2hcbiAgICAgICAgKX1gXG4gICAgICApXG4gICAgfVxuICB9IGVsc2UgaWYgKHBhdGNoLnBhdGgubGVuZ3RoID09PSAxKSB7XG4gICAgaWYgKHlqcyBpbnN0YW5jZW9mIFkuTWFwKSB7XG4gICAgICBjb25zdCBrZXkgPSBTdHJpbmcocGF0Y2gucGF0aFswXSlcblxuICAgICAgc3dpdGNoIChwYXRjaC5vcCkge1xuICAgICAgICBjYXNlIFwiYWRkXCI6XG4gICAgICAgIGNhc2UgXCJyZXBsYWNlXCI6IHtcbiAgICAgICAgICB5anMuc2V0KGtleSwgY29udmVydEpzb25Ub1lqc0RhdGEocGF0Y2gudmFsdWUgYXMgUGxhaW5WYWx1ZSkpXG4gICAgICAgICAgYnJlYWtcbiAgICAgICAgfVxuICAgICAgICBjYXNlIFwicmVtb3ZlXCI6IHtcbiAgICAgICAgICB5anMuZGVsZXRlKGtleSlcbiAgICAgICAgICBicmVha1xuICAgICAgICB9XG4gICAgICAgIGRlZmF1bHQ6IHtcbiAgICAgICAgICB0aHJvdyBmYWlsdXJlKGBpbnZhbGlkIHBhdGNoIG9wZXJhdGlvbiBmb3IgbWFwYClcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gZWxzZSBpZiAoeWpzIGluc3RhbmNlb2YgWS5BcnJheSkge1xuICAgICAgY29uc3Qga2V5ID0gcGF0Y2gucGF0aFswXVxuXG4gICAgICBzd2l0Y2ggKHBhdGNoLm9wKSB7XG4gICAgICAgIGNhc2UgXCJyZXBsYWNlXCI6IHtcbiAgICAgICAgICBpZiAoa2V5ID09PSBcImxlbmd0aFwiKSB7XG4gICAgICAgICAgICBjb25zdCBuZXdMZW5ndGggPSBwYXRjaC52YWx1ZSBhcyBudW1iZXJcbiAgICAgICAgICAgIGlmICh5anMubGVuZ3RoID4gbmV3TGVuZ3RoKSB7XG4gICAgICAgICAgICAgIGNvbnN0IHRvRGVsZXRlID0geWpzLmxlbmd0aCAtIG5ld0xlbmd0aFxuICAgICAgICAgICAgICB5anMuZGVsZXRlKG5ld0xlbmd0aCwgdG9EZWxldGUpXG4gICAgICAgICAgICB9IGVsc2UgaWYgKHlqcy5sZW5ndGggPCBwYXRjaC52YWx1ZSkge1xuICAgICAgICAgICAgICBjb25zdCB0b0luc2VydCA9IHBhdGNoLnZhbHVlIC0geWpzLmxlbmd0aFxuICAgICAgICAgICAgICB5anMuaW5zZXJ0KHlqcy5sZW5ndGgsIEFycmF5LmZyb20oeyBsZW5ndGg6IHRvSW5zZXJ0IH0pLmZpbGwodW5kZWZpbmVkKSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgeWpzLmRlbGV0ZShOdW1iZXIoa2V5KSlcbiAgICAgICAgICAgIHlqcy5pbnNlcnQoTnVtYmVyKGtleSksIFtjb252ZXJ0SnNvblRvWWpzRGF0YShwYXRjaC52YWx1ZSBhcyBQbGFpblZhbHVlKV0pXG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrXG4gICAgICAgIH1cbiAgICAgICAgY2FzZSBcImFkZFwiOiB7XG4gICAgICAgICAgeWpzLmluc2VydChOdW1iZXIoa2V5KSwgW2NvbnZlcnRKc29uVG9ZanNEYXRhKHBhdGNoLnZhbHVlIGFzIFBsYWluVmFsdWUpXSlcbiAgICAgICAgICBicmVha1xuICAgICAgICB9XG4gICAgICAgIGNhc2UgXCJyZW1vdmVcIjoge1xuICAgICAgICAgIHlqcy5kZWxldGUoTnVtYmVyKGtleSkpXG4gICAgICAgICAgYnJlYWtcbiAgICAgICAgfVxuICAgICAgICBkZWZhdWx0OiB7XG4gICAgICAgICAgdGhyb3cgZmFpbHVyZShgaW52YWxpZCBwYXRjaCBvcGVyYXRpb24gZm9yIGFycmF5YClcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gZWxzZSBpZiAoeWpzIGluc3RhbmNlb2YgWS5UZXh0KSB7XG4gICAgICAvLyBpbml0aWFsaXphdGlvbiBvZiBhIFlqc1RleHRNb2RlbCwgZG8gbm90aGluZ1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBmYWlsdXJlKFxuICAgICAgICBgaW52YWxpZCBwYXRjaCBwYXRoLCB0aGUgWWpzIG9iamVjdCBpcyBvZiBhbiB1bmtvd24gdHlwZSwgc28ga2V5IFwiJHtTdHJpbmcocGF0Y2gucGF0aFswXSl9XCIgY2Fubm90IGJlIGZvdW5kIGluIGl0YFxuICAgICAgKVxuICAgIH1cbiAgfSBlbHNlIHtcbiAgICB0aHJvdyBmYWlsdXJlKGBpbnZhbGlkIHBhdGNoIHBhdGgsIGl0IGNhbm5vdCBiZSBlbXB0eWApXG4gIH1cbn1cbiIsImltcG9ydCB7IG1vZGVsU25hcHNob3RPdXRXaXRoTWV0YWRhdGEgfSBmcm9tIFwibW9ieC1rZXlzdG9uZVwiXG5pbXBvcnQgKiBhcyBZIGZyb20gXCJ5anNcIlxuaW1wb3J0IHsgUGxhaW5PYmplY3QsIFBsYWluVmFsdWUgfSBmcm9tIFwiLi4vcGxhaW5UeXBlc1wiXG5pbXBvcnQgeyBZanNUZXh0TW9kZWwgfSBmcm9tIFwiLi9ZanNUZXh0TW9kZWxcIlxuaW1wb3J0IHsgYWN0aW9uIH0gZnJvbSBcIm1vYnhcIlxuXG5leHBvcnQgdHlwZSBZanNEYXRhID0gWS5BcnJheTxhbnk+IHwgWS5NYXA8YW55PiB8IFkuVGV4dCB8IFBsYWluVmFsdWVcblxuZXhwb3J0IGNvbnN0IGNvbnZlcnRZanNEYXRhVG9Kc29uID0gYWN0aW9uKCh5anNEYXRhOiBZanNEYXRhKTogUGxhaW5WYWx1ZSA9PiB7XG4gIGlmICh5anNEYXRhIGluc3RhbmNlb2YgWS5BcnJheSkge1xuICAgIHJldHVybiB5anNEYXRhLm1hcCgodikgPT4gY29udmVydFlqc0RhdGFUb0pzb24odikpXG4gIH1cblxuICBpZiAoeWpzRGF0YSBpbnN0YW5jZW9mIFkuTWFwKSB7XG4gICAgY29uc3Qgb2JqOiBQbGFpbk9iamVjdCA9IHt9XG4gICAgeWpzRGF0YS5mb3JFYWNoKCh2LCBrKSA9PiB7XG4gICAgICBvYmpba10gPSBjb252ZXJ0WWpzRGF0YVRvSnNvbih2KVxuICAgIH0pXG4gICAgcmV0dXJuIG9ialxuICB9XG5cbiAgaWYgKHlqc0RhdGEgaW5zdGFuY2VvZiBZLlRleHQpIHtcbiAgICBjb25zdCBkZWx0YXMgPSB5anNEYXRhLnRvRGVsdGEoKSBhcyB1bmtub3duW11cblxuICAgIHJldHVybiBtb2RlbFNuYXBzaG90T3V0V2l0aE1ldGFkYXRhKFlqc1RleHRNb2RlbCwge1xuICAgICAgZGVsdGFMaXN0OiBkZWx0YXMubGVuZ3RoID4gMCA/IFt7ICRmcm96ZW46IHRydWUsIGRhdGE6IGRlbHRhcyB9XSA6IFtdLFxuICAgIH0pIGFzIHVua25vd24gYXMgUGxhaW5WYWx1ZVxuICB9XG5cbiAgLy8gYXNzdW1lIGl0J3MgYSBwcmltaXRpdmVcbiAgcmV0dXJuIHlqc0RhdGFcbn0pXG4iLCJpbXBvcnQgeyBQYXRjaCB9IGZyb20gXCJtb2J4LWtleXN0b25lXCJcbmltcG9ydCAqIGFzIFkgZnJvbSBcInlqc1wiXG5pbXBvcnQgeyBQbGFpbkFycmF5LCBQbGFpbk9iamVjdCwgUGxhaW5WYWx1ZSB9IGZyb20gXCIuLi9wbGFpblR5cGVzXCJcbmltcG9ydCB7IGZhaWx1cmUgfSBmcm9tIFwiLi4vdXRpbHMvZXJyb3JcIlxuXG5leHBvcnQgZnVuY3Rpb24gY29udmVydFlqc0V2ZW50VG9QYXRjaGVzKGV2ZW50OiBZLllFdmVudDxhbnk+KTogUGF0Y2hbXSB7XG4gIGNvbnN0IHBhdGNoZXM6IFBhdGNoW10gPSBbXVxuXG4gIGlmIChldmVudCBpbnN0YW5jZW9mIFkuWU1hcEV2ZW50KSB7XG4gICAgY29uc3Qgc291cmNlID0gZXZlbnQudGFyZ2V0XG5cbiAgICBldmVudC5jaGFuZ2VzLmtleXMuZm9yRWFjaCgoY2hhbmdlLCBrZXkpID0+IHtcbiAgICAgIGNvbnN0IHBhdGggPSBbLi4uZXZlbnQucGF0aCwga2V5XVxuXG4gICAgICBzd2l0Y2ggKGNoYW5nZS5hY3Rpb24pIHtcbiAgICAgICAgY2FzZSBcImFkZFwiOlxuICAgICAgICAgIHBhdGNoZXMucHVzaCh7XG4gICAgICAgICAgICBvcDogXCJhZGRcIixcbiAgICAgICAgICAgIHBhdGgsXG4gICAgICAgICAgICB2YWx1ZTogdG9QbGFpblZhbHVlKHNvdXJjZS5nZXQoa2V5KSksXG4gICAgICAgICAgfSlcbiAgICAgICAgICBicmVha1xuXG4gICAgICAgIGNhc2UgXCJ1cGRhdGVcIjpcbiAgICAgICAgICBwYXRjaGVzLnB1c2goe1xuICAgICAgICAgICAgb3A6IFwicmVwbGFjZVwiLFxuICAgICAgICAgICAgcGF0aCxcbiAgICAgICAgICAgIHZhbHVlOiB0b1BsYWluVmFsdWUoc291cmNlLmdldChrZXkpKSxcbiAgICAgICAgICB9KVxuICAgICAgICAgIGJyZWFrXG5cbiAgICAgICAgY2FzZSBcImRlbGV0ZVwiOlxuICAgICAgICAgIHBhdGNoZXMucHVzaCh7XG4gICAgICAgICAgICBvcDogXCJyZW1vdmVcIixcbiAgICAgICAgICAgIHBhdGgsXG4gICAgICAgICAgfSlcbiAgICAgICAgICBicmVha1xuXG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgdGhyb3cgZmFpbHVyZShgdW5zdXBwb3J0ZWQgWWpzIG1hcCBldmVudCBhY3Rpb246ICR7Y2hhbmdlLmFjdGlvbn1gKVxuICAgICAgfVxuICAgIH0pXG4gIH0gZWxzZSBpZiAoZXZlbnQgaW5zdGFuY2VvZiBZLllBcnJheUV2ZW50KSB7XG4gICAgbGV0IHJldGFpbiA9IDBcbiAgICBldmVudC5jaGFuZ2VzLmRlbHRhLmZvckVhY2goKGNoYW5nZSkgPT4ge1xuICAgICAgaWYgKGNoYW5nZS5yZXRhaW4pIHtcbiAgICAgICAgcmV0YWluICs9IGNoYW5nZS5yZXRhaW5cbiAgICAgIH1cblxuICAgICAgaWYgKGNoYW5nZS5kZWxldGUpIHtcbiAgICAgICAgLy8gcmVtb3ZlIFggaXRlbXMgYXQgcmV0YWluIHBvc2l0aW9uXG4gICAgICAgIGNvbnN0IHBhdGggPSBbLi4uZXZlbnQucGF0aCwgcmV0YWluXVxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGNoYW5nZS5kZWxldGU7IGkrKykge1xuICAgICAgICAgIHBhdGNoZXMucHVzaCh7XG4gICAgICAgICAgICBvcDogXCJyZW1vdmVcIixcbiAgICAgICAgICAgIHBhdGgsXG4gICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAoY2hhbmdlLmluc2VydCkge1xuICAgICAgICBjb25zdCBuZXdWYWx1ZXMgPSBBcnJheS5pc0FycmF5KGNoYW5nZS5pbnNlcnQpID8gY2hhbmdlLmluc2VydCA6IFtjaGFuZ2UuaW5zZXJ0XVxuICAgICAgICBuZXdWYWx1ZXMuZm9yRWFjaCgodikgPT4ge1xuICAgICAgICAgIGNvbnN0IHBhdGggPSBbLi4uZXZlbnQucGF0aCwgcmV0YWluXVxuICAgICAgICAgIHBhdGNoZXMucHVzaCh7XG4gICAgICAgICAgICBvcDogXCJhZGRcIixcbiAgICAgICAgICAgIHBhdGgsXG4gICAgICAgICAgICB2YWx1ZTogdG9QbGFpblZhbHVlKHYpLFxuICAgICAgICAgIH0pXG4gICAgICAgICAgcmV0YWluKytcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICB9KVxuICB9IGVsc2UgaWYgKGV2ZW50IGluc3RhbmNlb2YgWS5ZVGV4dEV2ZW50KSB7XG4gICAgY29uc3QgcGF0aCA9IFsuLi5ldmVudC5wYXRoLCBcImRlbHRhTGlzdFwiLCAtMSAvKiBsYXN0IGl0ZW0gKi9dXG4gICAgcGF0Y2hlcy5wdXNoKHtcbiAgICAgIG9wOiBcImFkZFwiLFxuICAgICAgcGF0aCxcbiAgICAgIHZhbHVlOiB7ICRmcm96ZW46IHRydWUsIGRhdGE6IGV2ZW50LmRlbHRhIH0sXG4gICAgfSlcbiAgfVxuXG4gIHJldHVybiBwYXRjaGVzXG59XG5cbmZ1bmN0aW9uIHRvUGxhaW5WYWx1ZSh2OiBZLk1hcDxhbnk+IHwgWS5BcnJheTxhbnk+IHwgUGxhaW5WYWx1ZSkge1xuICBpZiAodiBpbnN0YW5jZW9mIFkuTWFwIHx8IHYgaW5zdGFuY2VvZiBZLkFycmF5KSB7XG4gICAgcmV0dXJuIHYudG9KU09OKCkgYXMgUGxhaW5PYmplY3QgfCBQbGFpbkFycmF5XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIHZcbiAgfVxufVxuIiwiaW1wb3J0IHsgYWN0aW9uIH0gZnJvbSBcIm1vYnhcIlxuaW1wb3J0IHtcbiAgQW55RGF0YU1vZGVsLFxuICBBbnlNb2RlbCxcbiAgQW55U3RhbmRhcmRUeXBlLFxuICBNb2RlbENsYXNzLFxuICBQYXRjaCxcbiAgU25hcHNob3RJbk9mLFxuICBUeXBlVG9EYXRhLFxuICBhcHBseVBhdGNoZXMsXG4gIGZyb21TbmFwc2hvdCxcbiAgZ2V0UGFyZW50VG9DaGlsZFBhdGgsXG4gIG9uR2xvYmFsUGF0Y2hlcyxcbiAgb25QYXRjaGVzLFxuICBvblNuYXBzaG90LFxufSBmcm9tIFwibW9ieC1rZXlzdG9uZVwiXG5pbXBvcnQgKiBhcyBZIGZyb20gXCJ5anNcIlxuaW1wb3J0IHsgZ2V0WWpzQ29sbGVjdGlvbkF0b20gfSBmcm9tIFwiLi4vdXRpbHMvZ2V0T3JDcmVhdGVZanNDb2xsZWN0aW9uQXRvbVwiXG5pbXBvcnQgeyBhcHBseU1vYnhLZXlzdG9uZVBhdGNoVG9ZanNPYmplY3QgfSBmcm9tIFwiLi9hcHBseU1vYnhLZXlzdG9uZVBhdGNoVG9ZanNPYmplY3RcIlxuaW1wb3J0IHsgY29udmVydFlqc0RhdGFUb0pzb24gfSBmcm9tIFwiLi9jb252ZXJ0WWpzRGF0YVRvSnNvblwiXG5pbXBvcnQgeyBjb252ZXJ0WWpzRXZlbnRUb1BhdGNoZXMgfSBmcm9tIFwiLi9jb252ZXJ0WWpzRXZlbnRUb1BhdGNoZXNcIlxuaW1wb3J0IHsgWWpzQmluZGluZ0NvbnRleHQsIHlqc0JpbmRpbmdDb250ZXh0IH0gZnJvbSBcIi4veWpzQmluZGluZ0NvbnRleHRcIlxuXG4vKipcbiAqIENyZWF0ZXMgYSBiaWRpcmVjdGlvbmFsIGJpbmRpbmcgYmV0d2VlbiBhIFkuanMgZGF0YSBzdHJ1Y3R1cmUgYW5kIGEgbW9ieC1rZXlzdG9uZSBtb2RlbC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJpbmRZanNUb01vYnhLZXlzdG9uZTxcbiAgVFR5cGUgZXh0ZW5kcyBBbnlTdGFuZGFyZFR5cGUgfCBNb2RlbENsYXNzPEFueU1vZGVsPiB8IE1vZGVsQ2xhc3M8QW55RGF0YU1vZGVsPixcbj4oe1xuICB5anNEb2MsXG4gIHlqc09iamVjdCxcbiAgbW9ieEtleXN0b25lVHlwZSxcbn06IHtcbiAgLyoqXG4gICAqIFRoZSBZLmpzIGRvY3VtZW50LlxuICAgKi9cbiAgeWpzRG9jOiBZLkRvY1xuICAvKipcbiAgICogVGhlIGJvdW5kIFkuanMgZGF0YSBzdHJ1Y3R1cmUuXG4gICAqL1xuICB5anNPYmplY3Q6IFkuTWFwPGFueT4gfCBZLkFycmF5PGFueT4gfCBZLlRleHRcbiAgLyoqXG4gICAqIFRoZSBtb2J4LWtleXN0b25lIG1vZGVsIHR5cGUuXG4gICAqL1xuICBtb2J4S2V5c3RvbmVUeXBlOiBUVHlwZVxufSk6IHtcbiAgLyoqXG4gICAqIFRoZSBib3VuZCBtb2J4LWtleXN0b25lIGluc3RhbmNlLlxuICAgKi9cbiAgYm91bmRPYmplY3Q6IFR5cGVUb0RhdGE8VFR5cGU+XG4gIC8qKlxuICAgKiBEaXNwb3NlcyB0aGUgYmluZGluZy5cbiAgICovXG4gIGRpc3Bvc2U6ICgpID0+IHZvaWRcbiAgLyoqXG4gICAqIFRoZSBZLmpzIG9yaWdpbiBzeW1ib2wgdXNlZCBmb3IgYmluZGluZyB0cmFuc2FjdGlvbnMuXG4gICAqL1xuICB5anNPcmlnaW46IHN5bWJvbFxufSB7XG4gIGNvbnN0IHlqc09yaWdpbiA9IFN5bWJvbChcImJpbmRZanNUb01vYnhLZXlzdG9uZVRyYW5zYWN0aW9uT3JpZ2luXCIpXG5cbiAgbGV0IGFwcGx5aW5nWWpzQ2hhbmdlc1RvTW9ieEtleXN0b25lID0gMFxuXG4gIGNvbnN0IGJpbmRpbmdDb250ZXh0OiBZanNCaW5kaW5nQ29udGV4dCA9IHtcbiAgICB5anNEb2MsXG4gICAgeWpzT2JqZWN0LFxuICAgIG1vYnhLZXlzdG9uZVR5cGUsXG4gICAgeWpzT3JpZ2luLFxuICAgIGJvdW5kT2JqZWN0OiB1bmRlZmluZWQsIC8vIG5vdCB5ZXQgY3JlYXRlZFxuXG4gICAgZ2V0IGlzQXBwbHlpbmdZanNDaGFuZ2VzVG9Nb2J4S2V5c3RvbmUoKSB7XG4gICAgICByZXR1cm4gYXBwbHlpbmdZanNDaGFuZ2VzVG9Nb2J4S2V5c3RvbmUgPiAwXG4gICAgfSxcbiAgfVxuXG4gIGNvbnN0IHlqc0pzb24gPSBjb252ZXJ0WWpzRGF0YVRvSnNvbih5anNPYmplY3QpXG5cbiAgY29uc3QgaW5pdGlhbGl6YXRpb25HbG9iYWxQYXRjaGVzOiB7IHRhcmdldDogb2JqZWN0OyBwYXRjaGVzOiBQYXRjaFtdIH1bXSA9IFtdXG5cbiAgY29uc3QgY3JlYXRlQm91bmRPYmplY3QgPSAoKSA9PiB7XG4gICAgY29uc3QgZGlzcG9zZU9uR2xvYmFsUGF0Y2hlcyA9IG9uR2xvYmFsUGF0Y2hlcygodGFyZ2V0LCBwYXRjaGVzKSA9PiB7XG4gICAgICBpbml0aWFsaXphdGlvbkdsb2JhbFBhdGNoZXMucHVzaCh7IHRhcmdldCwgcGF0Y2hlcyB9KVxuICAgIH0pXG5cbiAgICB0cnkge1xuICAgICAgY29uc3QgYm91bmRPYmplY3QgPSB5anNCaW5kaW5nQ29udGV4dC5hcHBseShcbiAgICAgICAgKCkgPT4gZnJvbVNuYXBzaG90KG1vYnhLZXlzdG9uZVR5cGUsIHlqc0pzb24gYXMgdW5rbm93biBhcyBTbmFwc2hvdEluT2Y8VHlwZVRvRGF0YTxUVHlwZT4+KSxcbiAgICAgICAgYmluZGluZ0NvbnRleHRcbiAgICAgIClcbiAgICAgIHlqc0JpbmRpbmdDb250ZXh0LnNldChib3VuZE9iamVjdCwgeyAuLi5iaW5kaW5nQ29udGV4dCwgYm91bmRPYmplY3QgfSlcbiAgICAgIHJldHVybiBib3VuZE9iamVjdFxuICAgIH0gZmluYWxseSB7XG4gICAgICBkaXNwb3NlT25HbG9iYWxQYXRjaGVzKClcbiAgICB9XG4gIH1cblxuICBjb25zdCBib3VuZE9iamVjdCA9IGNyZWF0ZUJvdW5kT2JqZWN0KClcblxuICAvLyBiaW5kIGFueSBjaGFuZ2VzIGZyb20geWpzIHRvIG1vYngta2V5c3RvbmVcbiAgY29uc3Qgb2JzZXJ2ZURlZXBDYiA9IGFjdGlvbigoZXZlbnRzOiBZLllFdmVudDxhbnk+W10pID0+IHtcbiAgICBjb25zdCBwYXRjaGVzOiBQYXRjaFtdID0gW11cbiAgICBldmVudHMuZm9yRWFjaCgoZXZlbnQpID0+IHtcbiAgICAgIGlmIChldmVudC50cmFuc2FjdGlvbi5vcmlnaW4gIT09IHlqc09yaWdpbikge1xuICAgICAgICBwYXRjaGVzLnB1c2goLi4uY29udmVydFlqc0V2ZW50VG9QYXRjaGVzKGV2ZW50KSlcbiAgICAgIH1cblxuICAgICAgaWYgKGV2ZW50LnRhcmdldCBpbnN0YW5jZW9mIFkuTWFwIHx8IGV2ZW50LnRhcmdldCBpbnN0YW5jZW9mIFkuQXJyYXkpIHtcbiAgICAgICAgZ2V0WWpzQ29sbGVjdGlvbkF0b20oZXZlbnQudGFyZ2V0KT8ucmVwb3J0Q2hhbmdlZCgpXG4gICAgICB9XG4gICAgfSlcblxuICAgIGlmIChwYXRjaGVzLmxlbmd0aCA+IDApIHtcbiAgICAgIGFwcGx5aW5nWWpzQ2hhbmdlc1RvTW9ieEtleXN0b25lKytcbiAgICAgIHRyeSB7XG4gICAgICAgIGFwcGx5UGF0Y2hlcyhib3VuZE9iamVjdCwgcGF0Y2hlcylcbiAgICAgIH0gZmluYWxseSB7XG4gICAgICAgIGFwcGx5aW5nWWpzQ2hhbmdlc1RvTW9ieEtleXN0b25lLS1cbiAgICAgIH1cbiAgICB9XG4gIH0pXG5cbiAgeWpzT2JqZWN0Lm9ic2VydmVEZWVwKG9ic2VydmVEZWVwQ2IpXG5cbiAgLy8gYmluZCBhbnkgY2hhbmdlcyBmcm9tIG1vYngta2V5c3RvbmUgdG8geWpzXG4gIGxldCBwZW5kaW5nQXJyYXlPZkFycmF5T2ZQYXRjaGVzOiBQYXRjaFtdW10gPSBbXVxuICBjb25zdCBkaXNwb3NlT25QYXRjaGVzID0gb25QYXRjaGVzKGJvdW5kT2JqZWN0LCAocGF0Y2hlcykgPT4ge1xuICAgIGlmIChhcHBseWluZ1lqc0NoYW5nZXNUb01vYnhLZXlzdG9uZSA+IDApIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIHBlbmRpbmdBcnJheU9mQXJyYXlPZlBhdGNoZXMucHVzaChwYXRjaGVzKVxuICB9KVxuXG4gIC8vIHRoaXMgaXMgb25seSB1c2VkIHNvIHdlIGNhbiB0cmFuc2FjdCBhbGwgcGF0Y2hlcyB0byB0aGUgc2