mobx-keystone-yjs
Version:
Yjs bindings for mobx-keystone
742 lines • 97.8 kB
JavaScript
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, remove } from "mobx";
import { createContext, Model, tProp, types, frozen, getParentToChildPath, onSnapshot, model, frozenKey, modelTypeKey, DeepChangeType, modelSnapshotOutWithMetadata, resolvePath, runUnprotected, isModel, getSnapshot, getSnapshotModelId, isFrozenSnapshot, fromSnapshot, onGlobalDeepChange, onDeepChange, isTreeNode } from "mobx-keystone";
import * as Y from "yjs";
class MobxKeystoneYjsError extends Error {
constructor(msg) {
super(msg);
Object.setPrototypeOf(this, MobxKeystoneYjsError.prototype);
}
}
function failure(msg) {
return new MobxKeystoneYjsError(msg);
}
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 isYjsValueDeleted(yjsValue) {
var _a, _b;
if (yjsValue instanceof Y.AbstractType) {
return !!((_a = yjsValue._item) == null ? void 0 : _a.deleted) || !!((_b = yjsValue.doc) == null ? void 0 : _b.isDestroyed);
}
return false;
}
function resolveYjsPath(yjsObject, path) {
let currentYjsObject = yjsObject;
let i = -1;
for (const pathPart of path) {
i++;
if (currentYjsObject instanceof Y.Text) {
return currentYjsObject;
}
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 yjsBindingContext = createContext(void 0);
var __defProp2 = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
if (kind && result) __defProp2(target, key, result);
return result;
};
const deltaListType = types.array(types.frozen(types.unchecked()));
const yjsTextModelId = "mobx-keystone-yjs/YjsTextModel";
let YjsTextModel = class 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
}
])
]
});
}
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;
}
get _yjsObjectAtPath() {
const path = this._yjsObjectPath;
const ctx = yjsBindingContext.get(this);
return resolveYjsPath(ctx.yjsObject, path);
}
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;
}
get text() {
this.yjsTextChangedAtom.reportObserved();
const ctx = yjsBindingContext.get(this);
if ((ctx == null ? void 0 : ctx.boundObject) != null) {
try {
const yjsTextString = this.yjsText.toString();
if (yjsTextString !== "" || this.deltaList.length === 0) {
return yjsTextString;
}
} catch {
}
}
return this.deltaListToText();
}
deltaListToText() {
const doc = new Y.Doc();
const text = doc.getText();
this.deltaList.forEach((d) => {
text.applyDelta(d.data);
});
return text.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;
if (isYjsValueDeleted(yjsText)) {
throw failure("cannot reapply deltas to deleted Yjs.Text");
}
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;
if (isYjsValueDeleted(yjsText)) {
throw failure("cannot reapply deltas to deleted Yjs.Text");
}
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();
};
}
};
__decorateClass([
computed
], YjsTextModel.prototype, "_yjsObjectPath", 1);
__decorateClass([
computed
], YjsTextModel.prototype, "_yjsObjectAtPath", 1);
__decorateClass([
computed
], YjsTextModel.prototype, "yjsText", 1);
__decorateClass([
computed
], YjsTextModel.prototype, "text", 1);
YjsTextModel = __decorateClass([
model(yjsTextModelId)
], YjsTextModel);
const DecoratedYjsTextModel = YjsTextModel;
function hookYjsTextChangedAtom(getYjsText, textChangedAtom) {
let disposeObserveYjsText;
const observeFn = () => {
textChangedAtom.reportChanged();
};
const disposeReactionToYTextChange = reaction(
() => {
try {
const yjsText = getYjsText();
return isYjsValueDeleted(yjsText) ? void 0 : yjsText;
} 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;
};
}
const yjsContainerToSnapshot = /* @__PURE__ */ new WeakMap();
function setYjsContainerSnapshot(container, snapshot) {
yjsContainerToSnapshot.set(container, snapshot);
}
function isYjsContainerUpToDate(container, snapshot) {
return yjsContainerToSnapshot.get(container) === snapshot;
}
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 typeof v === "object" && v !== null && !Array.isArray(v);
}
function convertJsonToYjsData(v) {
if (isPlainPrimitive(v)) {
return v;
}
if (isPlainArray(v)) {
const arr = new Y.Array();
applyJsonArrayToYArray(arr, v);
return arr;
}
if (isPlainObject(v)) {
if (v[frozenKey] === true) {
return v;
}
if (v[modelTypeKey] === 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 = (dest, source, options = {}) => {
const { mode = "add" } = options;
if (mode === "merge" && isYjsContainerUpToDate(dest, source)) {
return;
}
const srcLen = source.length;
if (mode === "add") {
for (let i = 0; i < srcLen; i++) {
dest.push([convertJsonToYjsData(source[i])]);
}
return;
}
const destLen = dest.length;
if (destLen > srcLen) {
dest.delete(srcLen, destLen - srcLen);
}
const minLen = Math.min(destLen, srcLen);
for (let i = 0; i < minLen; i++) {
const srcItem = source[i];
const destItem = dest.get(i);
if (isPlainObject(srcItem) && destItem instanceof Y.Map) {
applyJsonObjectToYMap(destItem, srcItem, options);
continue;
}
if (isPlainArray(srcItem) && destItem instanceof Y.Array) {
applyJsonArrayToYArray(destItem, srcItem, options);
continue;
}
if (isPlainPrimitive(srcItem) && destItem === srcItem) {
continue;
}
dest.delete(i, 1);
dest.insert(i, [convertJsonToYjsData(srcItem)]);
}
for (let i = destLen; i < srcLen; i++) {
dest.push([convertJsonToYjsData(source[i])]);
}
setYjsContainerSnapshot(dest, source);
};
const applyJsonObjectToYMap = (dest, source, options = {}) => {
const { mode = "add" } = options;
if (mode === "merge" && isYjsContainerUpToDate(dest, source)) {
return;
}
if (mode === "add") {
for (const k of Object.keys(source)) {
const v = source[k];
if (v !== void 0) {
dest.set(k, convertJsonToYjsData(v));
}
}
return;
}
const sourceKeysWithValues = new Set(Object.keys(source).filter((k) => source[k] !== void 0));
for (const key of dest.keys()) {
if (!sourceKeysWithValues.has(key)) {
dest.delete(key);
}
}
for (const k of Object.keys(source)) {
const v = source[k];
if (v === void 0) {
continue;
}
const existing = dest.get(k);
if (isPlainObject(v) && existing instanceof Y.Map) {
applyJsonObjectToYMap(existing, v, options);
continue;
}
if (isPlainArray(v) && existing instanceof Y.Array) {
applyJsonArrayToYArray(existing, v, options);
continue;
}
if (isPlainPrimitive(v) && existing === v) {
continue;
}
dest.set(k, convertJsonToYjsData(v));
}
setYjsContainerSnapshot(dest, source);
};
function convertValue(v) {
if (v === null || v === void 0 || typeof v !== "object") {
return v;
}
if (Array.isArray(v) && v.length === 0) {
return new Y.Array();
}
return convertJsonToYjsData(v);
}
function applyMobxChangeToYjsObject(change, yjsObject) {
if (isYjsValueDeleted(yjsObject)) {
throw failure("cannot apply patch to deleted Yjs value");
}
const yjsContainer = resolveYjsPath(yjsObject, change.path);
if (!yjsContainer) {
return;
}
if (yjsContainer instanceof Y.Array) {
if (change.type === DeepChangeType.ArraySplice) {
yjsContainer.delete(change.index, change.removedValues.length);
if (change.addedValues.length > 0) {
const valuesToInsert = change.addedValues.map(convertValue);
yjsContainer.insert(change.index, valuesToInsert);
}
} else if (change.type === DeepChangeType.ArrayUpdate) {
yjsContainer.delete(change.index, 1);
yjsContainer.insert(change.index, [convertValue(change.newValue)]);
} else {
throw failure(`unsupported array change type: ${change.type}`);
}
} else if (yjsContainer instanceof Y.Map) {
if (change.type === DeepChangeType.ObjectAdd || change.type === DeepChangeType.ObjectUpdate) {
const key = change.key;
if (change.newValue === void 0) {
yjsContainer.delete(key);
} else {
yjsContainer.set(key, convertValue(change.newValue));
}
} else if (change.type === DeepChangeType.ObjectRemove) {
const key = change.key;
yjsContainer.delete(key);
} else {
throw failure(`unsupported object change type: ${change.type}`);
}
} else if (yjsContainer instanceof Y.Text) {
return;
} else {
throw failure(`unsupported Yjs container type: ${yjsContainer}`);
}
}
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 applyYjsEventToMobx(event, boundObject, reconciliationMap) {
const path = event.path;
const { value: target } = resolvePath(boundObject, path);
if (!target) {
throw failure(`cannot resolve path ${JSON.stringify(path)}`);
}
runUnprotected(() => {
if (event instanceof Y.YMapEvent) {
applyYMapEventToMobx(event, target, reconciliationMap);
} else if (event instanceof Y.YArrayEvent) {
applyYArrayEventToMobx(event, target, reconciliationMap);
} else if (event instanceof Y.YTextEvent) {
applyYTextEventToMobx(event, target);
}
});
}
function processDeletedValue(val, reconciliationMap) {
if (val && typeof val === "object" && isModel(val)) {
const sn = getSnapshot(val);
const id = getSnapshotModelId(sn);
if (id) {
reconciliationMap.set(id, val);
}
}
}
function reviveValue(jsonValue, reconciliationMap) {
if (jsonValue === null || typeof jsonValue !== "object") {
return jsonValue;
}
if (isFrozenSnapshot(jsonValue)) {
return frozen(jsonValue.data);
}
if (reconciliationMap && jsonValue && typeof jsonValue === "object") {
const modelId = getSnapshotModelId(jsonValue);
if (modelId) {
const existing = reconciliationMap.get(modelId);
if (existing) {
reconciliationMap.delete(modelId);
return existing;
}
}
}
return fromSnapshot(jsonValue);
}
function applyYMapEventToMobx(event, target, reconciliationMap) {
const source = event.target;
event.changes.keys.forEach((change, key) => {
switch (change.action) {
case "add":
case "update": {
const yjsValue = source.get(key);
const jsonValue = convertYjsDataToJson(yjsValue);
if (change.action === "update") {
processDeletedValue(target[key], reconciliationMap);
}
target[key] = reviveValue(jsonValue, reconciliationMap);
break;
}
case "delete": {
processDeletedValue(target[key], reconciliationMap);
if (isModel(target)) {
remove(target.$, key);
} else {
remove(target, key);
}
break;
}
default:
throw failure(`unsupported Yjs map event action: ${change.action}`);
}
});
}
function applyYArrayEventToMobx(event, target, reconciliationMap) {
let currentIndex = 0;
for (const change of event.changes.delta) {
if (change.retain) {
currentIndex += change.retain;
}
if (change.delete) {
const deletedItems = target.slice(currentIndex, currentIndex + change.delete);
deletedItems.forEach((item) => {
processDeletedValue(item, reconciliationMap);
});
target.splice(currentIndex, change.delete);
}
if (change.insert) {
const insertedItems = Array.isArray(change.insert) ? change.insert : [change.insert];
const values = insertedItems.map((yjsValue) => {
const jsonValue = convertYjsDataToJson(yjsValue);
return reviveValue(jsonValue, reconciliationMap);
});
target.splice(currentIndex, 0, ...values);
currentIndex += values.length;
}
}
}
function applyYTextEventToMobx(event, target) {
if (target == null ? void 0 : target.deltaList) {
target.deltaList.push(frozen(event.delta));
}
}
function captureChangeSnapshots(change) {
if (change.type === DeepChangeType.ArraySplice && change.addedValues.length > 0) {
const snapshots = change.addedValues.map((v) => isTreeNode(v) ? getSnapshot(v) : v);
return { ...change, addedValues: snapshots };
} else if (change.type === DeepChangeType.ArrayUpdate) {
const snapshot = isTreeNode(change.newValue) ? getSnapshot(change.newValue) : change.newValue;
return { ...change, newValue: snapshot };
} else if (change.type === DeepChangeType.ObjectAdd || change.type === DeepChangeType.ObjectUpdate) {
const snapshot = isTreeNode(change.newValue) ? getSnapshot(change.newValue) : change.newValue;
return { ...change, newValue: snapshot };
}
return change;
}
function bindYjsToMobxKeystone({
yjsDoc,
yjsObject,
mobxKeystoneType
}) {
const yjsOrigin = /* @__PURE__ */ Symbol("bindYjsToMobxKeystoneTransactionOrigin");
let applyingYjsChangesToMobxKeystone = 0;
const bindingContext = {
yjsDoc,
yjsObject,
mobxKeystoneType,
yjsOrigin,
boundObject: void 0,
// not yet created
get isApplyingYjsChangesToMobxKeystone() {
return applyingYjsChangesToMobxKeystone > 0;
}
};
if (isYjsValueDeleted(yjsObject)) {
throw failure("cannot apply patch to deleted Yjs value");
}
const yjsJson = convertYjsDataToJson(yjsObject);
let boundObject;
let hasInitChanges = false;
const createBoundObject = () => {
const disposeGlobalListener = onGlobalDeepChange((_target, change) => {
if (change.isInit) {
hasInitChanges = true;
}
});
try {
const result = yjsBindingContext.apply(
() => fromSnapshot(mobxKeystoneType, yjsJson),
bindingContext
);
yjsBindingContext.set(result, { ...bindingContext, boundObject: result });
return result;
} finally {
disposeGlobalListener();
}
};
boundObject = createBoundObject();
const observeDeepCb = action((events) => {
const eventsToApply = [];
events.forEach((event) => {
var _a;
if (event.transaction.origin !== yjsOrigin) {
eventsToApply.push(event);
}
if (event.target instanceof Y.Map || event.target instanceof Y.Array) {
(_a = getYjsCollectionAtom(event.target)) == null ? void 0 : _a.reportChanged();
}
});
if (eventsToApply.length > 0) {
applyingYjsChangesToMobxKeystone++;
try {
const reconciliationMap = /* @__PURE__ */ new Map();
const initChanges = [];
const disposeGlobalListener = onGlobalDeepChange((target, change) => {
if (change.isInit) {
initChanges.push({ target, change: captureChangeSnapshots(change) });
}
});
try {
eventsToApply.forEach((event) => {
applyYjsEventToMobx(event, boundObject, reconciliationMap);
});
} finally {
disposeGlobalListener();
}
if (initChanges.length > 0 && !isYjsValueDeleted(yjsObject)) {
yjsDoc.transact(() => {
for (const { target, change } of initChanges) {
const pathToTarget = getParentToChildPath(boundObject, target);
if (pathToTarget !== void 0) {
const changeWithCorrectPath = {
...change,
path: [...pathToTarget, ...change.path]
};
applyMobxChangeToYjsObject(changeWithCorrectPath, yjsObject);
}
}
}, yjsOrigin);
}
if (yjsObject instanceof Y.Map || yjsObject instanceof Y.Array) {
setYjsContainerSnapshot(yjsObject, getSnapshot(boundObject));
}
} finally {
applyingYjsChangesToMobxKeystone--;
}
}
});
yjsObject.observeDeep(observeDeepCb);
let pendingChanges = [];
const disposeOnDeepChange = onDeepChange(boundObject, (change) => {
if (applyingYjsChangesToMobxKeystone > 0) {
return;
}
if (change.isInit) {
return;
}
pendingChanges.push(captureChangeSnapshots(change));
});
const disposeOnSnapshot = onSnapshot(boundObject, (boundObjectSnapshot) => {
if (pendingChanges.length === 0) {
return;
}
const changesToApply = pendingChanges;
pendingChanges = [];
if (isYjsValueDeleted(yjsObject)) {
return;
}
yjsDoc.transact(() => {
changesToApply.forEach((change) => {
applyMobxChangeToYjsObject(change, yjsObject);
});
}, yjsOrigin);
if (yjsObject instanceof Y.Map || yjsObject instanceof Y.Array) {
setYjsContainerSnapshot(yjsObject, boundObjectSnapshot);
}
});
const finalSnapshot = getSnapshot(boundObject);
if (hasInitChanges) {
yjsDoc.transact(() => {
if (yjsObject instanceof Y.Map) {
applyJsonObjectToYMap(yjsObject, finalSnapshot, {
mode: "merge"
});
} else if (yjsObject instanceof Y.Array) {
applyJsonArrayToYArray(yjsObject, finalSnapshot, {
mode: "merge"
});
}
}, yjsOrigin);
}
if (yjsObject instanceof Y.Map || yjsObject instanceof Y.Array) {
setYjsContainerSnapshot(yjsObject, finalSnapshot);
}
const dispose = () => {
yjsDoc.off("destroy", dispose);
disposeOnDeepChange();
disposeOnSnapshot();
yjsObject.unobserveDeep(observeDeepCb);
};
yjsDoc.on("destroy", dispose);
return {
boundObject,
dispose,
yjsOrigin
};
}
export {
MobxKeystoneYjsError,
YjsTextModel,
applyJsonArrayToYArray,
applyJsonObjectToYMap,
bindYjsToMobxKeystone,
convertJsonToYjsData,
yjsBindingContext,
yjsTextModelId
};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9ieC1rZXlzdG9uZS15anMuZXNtLm1qcyIsInNvdXJjZXMiOlsiLi4vc3JjL3V0aWxzL2Vycm9yLnRzIiwiLi4vc3JjL3V0aWxzL2dldE9yQ3JlYXRlWWpzQ29sbGVjdGlvbkF0b20udHMiLCIuLi9zcmMvdXRpbHMvaXNZanNWYWx1ZURlbGV0ZWQudHMiLCIuLi9zcmMvYmluZGluZy9yZXNvbHZlWWpzUGF0aC50cyIsIi4uL3NyYy9iaW5kaW5nL3lqc0JpbmRpbmdDb250ZXh0LnRzIiwiLi4vc3JjL2JpbmRpbmcvWWpzVGV4dE1vZGVsLnRzIiwiLi4vc3JjL2JpbmRpbmcveWpzU25hcHNob3RUcmFja2luZy50cyIsIi4uL3NyYy9iaW5kaW5nL2NvbnZlcnRKc29uVG9ZanNEYXRhLnRzIiwiLi4vc3JjL2JpbmRpbmcvYXBwbHlNb2J4Q2hhbmdlVG9ZanNPYmplY3QudHMiLCIuLi9zcmMvYmluZGluZy9jb252ZXJ0WWpzRGF0YVRvSnNvbi50cyIsIi4uL3NyYy9iaW5kaW5nL2FwcGx5WWpzRXZlbnRUb01vYngudHMiLCIuLi9zcmMvYmluZGluZy9iaW5kWWpzVG9Nb2J4S2V5c3RvbmUudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXHJcbiAqIEEgbW9ieC1rZXlzdG9uZS15anMgZXJyb3IuXHJcbiAqL1xyXG5leHBvcnQgY2xhc3MgTW9ieEtleXN0b25lWWpzRXJyb3IgZXh0ZW5kcyBFcnJvciB7XHJcbiAgY29uc3RydWN0b3IobXNnOiBzdHJpbmcpIHtcclxuICAgIHN1cGVyKG1zZylcclxuXHJcbiAgICAvLyBTZXQgdGhlIHByb3RvdHlwZSBleHBsaWNpdGx5LlxyXG4gICAgT2JqZWN0LnNldFByb3RvdHlwZU9mKHRoaXMsIE1vYnhLZXlzdG9uZVlqc0Vycm9yLnByb3RvdHlwZSlcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBAaW50ZXJuYWxcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBmYWlsdXJlKG1zZzogc3RyaW5nKSB7XHJcbiAgcmV0dXJuIG5ldyBNb2J4S2V5c3RvbmVZanNFcnJvcihtc2cpXHJcbn1cclxuIiwiaW1wb3J0IHsgY3JlYXRlQXRvbSwgSUF0b20gfSBmcm9tIFwibW9ieFwiXG5pbXBvcnQgKiBhcyBZIGZyb20gXCJ5anNcIlxuXG5jb25zdCB5anNDb2xsZWN0aW9uQXRvbXMgPSBuZXcgV2Vha01hcDxZLk1hcDx1bmtub3duPiB8IFkuQXJyYXk8dW5rbm93bj4sIElBdG9tPigpXG5cbi8qKlxuICogQGludGVybmFsXG4gKi9cbmV4cG9ydCBjb25zdCBnZXRZanNDb2xsZWN0aW9uQXRvbSA9IChcbiAgeWpzQ29sbGVjdGlvbjogWS5NYXA8dW5rbm93bj4gfCBZLkFycmF5PHVua25vd24+XG4pOiBJQXRvbSB8IHVuZGVmaW5lZCA9PiB7XG4gIHJldHVybiB5anNDb2xsZWN0aW9uQXRvbXMuZ2V0KHlqc0NvbGxlY3Rpb24pXG59XG5cbi8qKlxuICogQGludGVybmFsXG4gKi9cbmV4cG9ydCBjb25zdCBnZXRPckNyZWF0ZVlqc0NvbGxlY3Rpb25BdG9tID0gKFxuICB5anNDb2xsZWN0aW9uOiBZLk1hcDx1bmtub3duPiB8IFkuQXJyYXk8dW5rbm93bj5cbik6IElBdG9tID0+IHtcbiAgbGV0IGF0b20gPSB5anNDb2xsZWN0aW9uQXRvbXMuZ2V0KHlqc0NvbGxlY3Rpb24pXG4gIGlmICghYXRvbSkge1xuICAgIGF0b20gPSBjcmVhdGVBdG9tKGB5anNDb2xsZWN0aW9uQXRvbWApXG4gICAgeWpzQ29sbGVjdGlvbkF0b21zLnNldCh5anNDb2xsZWN0aW9uLCBhdG9tKVxuICB9XG4gIHJldHVybiBhdG9tXG59XG4iLCJpbXBvcnQgKiBhcyBZIGZyb20gXCJ5anNcIlxuXG4vKipcbiAqIENoZWNrcyBpZiBhIFkuanMgdmFsdWUgaGFzIGJlZW4gZGVsZXRlZCBvciBpdHMgZG9jdW1lbnQgZGVzdHJveWVkLlxuICpcbiAqIEBwYXJhbSB5anNWYWx1ZSBUaGUgWS5qcyB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIGB0cnVlYCBpZiB0aGUgdmFsdWUgaXMgZGVsZXRlZCBvciBkZXN0cm95ZWQsIGBmYWxzZWAgb3RoZXJ3aXNlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNZanNWYWx1ZURlbGV0ZWQoeWpzVmFsdWU6IHVua25vd24pOiBib29sZWFuIHtcbiAgaWYgKHlqc1ZhbHVlIGluc3RhbmNlb2YgWS5BYnN0cmFjdFR5cGUpIHtcbiAgICByZXR1cm4gISEoeWpzVmFsdWUgYXMgYW55KS5faXRlbT8uZGVsZXRlZCB8fCAhIXlqc1ZhbHVlLmRvYz8uaXNEZXN0cm95ZWRcbiAgfVxuICByZXR1cm4gZmFsc2Vcbn1cbiIsImltcG9ydCAqIGFzIFkgZnJvbSBcInlqc1wiXG5pbXBvcnQgeyBmYWlsdXJlIH0gZnJvbSBcIi4uL3V0aWxzL2Vycm9yXCJcbmltcG9ydCB7IGdldE9yQ3JlYXRlWWpzQ29sbGVjdGlvbkF0b20gfSBmcm9tIFwiLi4vdXRpbHMvZ2V0T3JDcmVhdGVZanNDb2xsZWN0aW9uQXRvbVwiXG5cbi8qKlxuICogUmVzb2x2ZXMgYSBwYXRoIHdpdGhpbiBhIFlqcyBvYmplY3Qgc3RydWN0dXJlLlxuICogUmV0dXJucyB0aGUgWWpzIGNvbnRhaW5lciBhdCB0aGUgc3BlY2lmaWVkIHBhdGguXG4gKlxuICogV2hlbiBhIFkuVGV4dCBpcyBlbmNvdW50ZXJlZCBkdXJpbmcgcGF0aCByZXNvbHV0aW9uIChlaXRoZXIgYXQgdGhlIHN0YXJ0XG4gKiBvciBtaWQtcGF0aCksIGl0IGlzIHJldHVybmVkIGltbWVkaWF0ZWx5IHNpbmNlIFkuVGV4dCBkb2Vzbid0IHN1cHBvcnRcbiAqIG5lc3RlZCBwYXRoIHRyYXZlcnNhbC5cbiAqXG4gKiBAcGFyYW0geWpzT2JqZWN0IFRoZSByb290IFlqcyBvYmplY3RcbiAqIEBwYXJhbSBwYXRoIEFycmF5IG9mIGtleXMvaW5kaWNlcyB0byB0cmF2ZXJzZVxuICogQHJldHVybnMgVGhlIFlqcyBjb250YWluZXIgYXQgdGhlIHBhdGgsIG9yIFkuVGV4dCBpZiBlbmNvdW50ZXJlZCBkdXJpbmcgdHJhdmVyc2FsXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlWWpzUGF0aChcbiAgeWpzT2JqZWN0OiBZLk1hcDx1bmtub3duPiB8IFkuQXJyYXk8dW5rbm93bj4gfCBZLlRleHQsXG4gIHBhdGg6IHJlYWRvbmx5IChzdHJpbmcgfCBudW1iZXIpW11cbik6IHVua25vd24ge1xuICBsZXQgY3VycmVudFlqc09iamVjdDogdW5rbm93biA9IHlqc09iamVjdFxuXG4gIGxldCBpID0gLTFcbiAgZm9yIChjb25zdCBwYXRoUGFydCBvZiBwYXRoKSB7XG4gICAgaSsrXG4gICAgLy8gSWYgd2UgZW5jb3VudGVyIGEgWS5UZXh0IGR1cmluZyBwYXRoIHJlc29sdXRpb24sIHJldHVybiBpdCBpbW1lZGlhdGVseS5cbiAgICAvLyBZLlRleHQgb2JqZWN0cyBkb24ndCBzdXBwb3J0IG5lc3RlZCBwYXRoIHRyYXZlcnNhbCwgYW5kIHRoZWlyIHVwZGF0ZXNcbiAgICAvLyBhcmUgaGFuZGxlZCBzZXBhcmF0ZWx5IGJ5IFlqc1RleHRNb2RlbCdzIG93biBzeW5jaHJvbml6YXRpb24gbWVjaGFuaXNtLlxuICAgIGlmIChjdXJyZW50WWpzT2JqZWN0IGluc3RhbmNlb2YgWS5UZXh0KSB7XG4gICAgICByZXR1cm4gY3VycmVudFlqc09iamVjdFxuICAgIH1cblxuICAgIGlmIChjdXJyZW50WWpzT2JqZWN0IGluc3RhbmNlb2YgWS5NYXApIHtcbiAgICAgIGdldE9yQ3JlYXRlWWpzQ29sbGVjdGlvbkF0b20oY3VycmVudFlqc09iamVjdCkucmVwb3J0T2JzZXJ2ZWQoKVxuICAgICAgY29uc3Qga2V5ID0gU3RyaW5nKHBhdGhQYXJ0KVxuICAgICAgY3VycmVudFlqc09iamVjdCA9IGN1cnJlbnRZanNPYmplY3QuZ2V0KGtleSlcbiAgICB9IGVsc2UgaWYgKGN1cnJlbnRZanNPYmplY3QgaW5zdGFuY2VvZiBZLkFycmF5KSB7XG4gICAgICBnZXRPckNyZWF0ZVlqc0NvbGxlY3Rpb25BdG9tKGN1cnJlbnRZanNPYmplY3QpLnJlcG9ydE9ic2VydmVkKClcbiAgICAgIGNvbnN0IGtleSA9IE51bWJlcihwYXRoUGFydClcbiAgICAgIGN1cnJlbnRZanNPYmplY3QgPSBjdXJyZW50WWpzT2JqZWN0LmdldChrZXkpXG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IGZhaWx1cmUoXG4gICAgICAgIGBZLk1hcCBvciBZLkFycmF5IHdhcyBleHBlY3RlZCBhdCBwYXRoICR7SlNPTi5zdHJpbmdpZnkoXG4gICAgICAgICAgcGF0aC5zbGljZSgwLCBpKVxuICAgICAgICApfSBpbiBvcmRlciB0byByZXNvbHZlIHBhdGggJHtKU09OLnN0cmluZ2lmeShwYXRoKX0sIGJ1dCBnb3QgJHtjdXJyZW50WWpzT2JqZWN0fSBpbnN0ZWFkYFxuICAgICAgKVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiBjdXJyZW50WWpzT2JqZWN0XG59XG4iLCJpbXBvcnQgeyBBbnlUeXBlLCBjcmVhdGVDb250ZXh0IH0gZnJvbSBcIm1vYngta2V5c3RvbmVcIlxuaW1wb3J0ICogYXMgWSBmcm9tIFwieWpzXCJcblxuLyoqXG4gKiBDb250ZXh0IHdpdGggaW5mbyBvbiBob3cgYSBtb2J4LWtleXN0b25lIG1vZGVsIGlzIGJvdW5kIHRvIGEgWS5qcyBkYXRhIHN0cnVjdHVyZS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBZanNCaW5kaW5nQ29udGV4dCB7XG4gIC8qKlxuICAgKiBUaGUgWS5qcyBkb2N1bWVudC5cbiAgICovXG4gIHlqc0RvYzogWS5Eb2NcblxuICAvKipcbiAgICogVGhlIGJvdW5kIFkuanMgZGF0YSBzdHJ1Y3R1cmUuXG4gICAqL1xuICB5anNPYmplY3Q6IFkuTWFwPHVua25vd24+IHwgWS5BcnJheTx1bmtub3duPiB8IFkuVGV4dFxuXG4gIC8qKlxuICAgKiBUaGUgbW9ieC1rZXlzdG9uZSBtb2RlbCB0eXBlLlxuICAgKi9cbiAgbW9ieEtleXN0b25lVHlwZTogQW55VHlwZVxuXG4gIC8qKlxuICAgKiBUaGUgb3JpZ2luIHN5bWJvbCB1c2VkIGZvciB0cmFuc2FjdGlvbnMuXG4gICAqL1xuICB5anNPcmlnaW46IHN5bWJvbFxuXG4gIC8qKlxuICAgKiBUaGUgYm91bmQgbW9ieC1rZXlzdG9uZSBpbnN0YW5jZS5cbiAgICovXG4gIGJvdW5kT2JqZWN0OiB1bmtub3duXG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgd2UgYXJlIGN1cnJlbnRseSBhcHBseWluZyBZLmpzIGNoYW5nZXMgdG8gdGhlIG1vYngta2V5c3RvbmUgbW9kZWwuXG4gICAqL1xuICBpc0FwcGx5aW5nWWpzQ2hhbmdlc1RvTW9ieEtleXN0b25lOiBib29sZWFuXG59XG5cbi8qKlxuICogQ29udGV4dCB3aXRoIGluZm8gb24gaG93IGEgbW9ieC1rZXlzdG9uZSBtb2RlbCBpcyBib3VuZCB0byBhIFkuanMgZGF0YSBzdHJ1Y3R1cmUuXG4gKi9cbmV4cG9ydCBjb25zdCB5anNCaW5kaW5nQ29udGV4dCA9IGNyZWF0ZUNvbnRleHQ8WWpzQmluZGluZ0NvbnRleHQgfCB1bmRlZmluZWQ+KHVuZGVmaW5lZClcbiIsImltcG9ydCB7IGNvbXB1dGVkLCBjcmVhdGVBdG9tLCBJQXRvbSwgb2JzZXJ2ZSwgcmVhY3Rpb24gfSBmcm9tIFwibW9ieFwiXG5pbXBvcnQge1xuICBGcm96ZW4sXG4gIGZyb3plbixcbiAgZ2V0UGFyZW50VG9DaGlsZFBhdGgsXG4gIE1vZGVsLFxuICBtb2RlbCxcbiAgb25TbmFwc2hvdCxcbiAgdFByb3AsXG4gIHR5cGVzLFxufSBmcm9tIFwibW9ieC1rZXlzdG9uZVwiXG5pbXBvcnQgKiBhcyBZIGZyb20gXCJ5anNcIlxuaW1wb3J0IHsgZmFpbHVyZSB9IGZyb20gXCIuLi91dGlscy9lcnJvclwiXG5pbXBvcnQgeyBpc1lqc1ZhbHVlRGVsZXRlZCB9IGZyb20gXCIuLi91dGlscy9pc1lqc1ZhbHVlRGVsZXRlZFwiXG5pbXBvcnQgeyByZXNvbHZlWWpzUGF0aCB9IGZyb20gXCIuL3Jlc29sdmVZanNQYXRoXCJcbmltcG9ydCB7IFlqc0JpbmRpbmdDb250ZXh0LCB5anNCaW5kaW5nQ29udGV4dCB9IGZyb20gXCIuL3lqc0JpbmRpbmdDb250ZXh0XCJcblxuLy8gRGVsdGFbXVtdLCBzaW5jZSBlYWNoIHNpbmdsZSBjaGFuZ2UgaXMgYSBEZWx0YVtdXG4vLyB3ZSB1c2UgZnJvemVuIHNvIHRoYXQgd2UgY2FuIHJldXNlIGVhY2ggZGVsdGEgY2hhbmdlXG5jb25zdCBkZWx0YUxpc3RUeXBlID0gdHlwZXMuYXJyYXkodHlwZXMuZnJvemVuKHR5cGVzLnVuY2hlY2tlZDx1bmtub3duW10+KCkpKVxuXG5leHBvcnQgY29uc3QgeWpzVGV4dE1vZGVsSWQgPSBcIm1vYngta2V5c3RvbmUteWpzL1lqc1RleHRNb2RlbFwiXG5cbi8qKlxuICogQSBtb2J4LWtleXN0b25lIG1vZGVsIHRoYXQgcmVwcmVzZW50cyBhIFlqcy5UZXh0IG9iamVjdC5cbiAqL1xuQG1vZGVsKHlqc1RleHRNb2RlbElkKVxuZXhwb3J0IGNsYXNzIFlqc1RleHRNb2RlbCBleHRlbmRzIE1vZGVsKHtcbiAgZGVsdGFMaXN0OiB0UHJvcChkZWx0YUxpc3RUeXBlLCAoKSA9PiBbXSksXG59KSB7XG4gIC8qKlxuICAgKiBIZWxwZXIgZnVuY3Rpb24gdG8gY3JlYXRlIGEgWWpzVGV4dE1vZGVsIGluc3RhbmNlIHdpdGggYSBzaW1wbGUgdGV4dC5cbiAgICovXG4gIHN0YXRpYyB3aXRoVGV4dCh0ZXh0OiBzdHJpbmcpOiBZanNUZXh0TW9kZWwge1xuICAgIHJldHVybiBuZXcgRGVjb3JhdGVkWWpzVGV4dE1vZGVsKHtcbiAgICAgIGRlbHRhTGlzdDogW1xuICAgICAgICBmcm96ZW4oW1xuICAgICAgICAgIHtcbiAgICAgICAgICAgIGluc2VydDogdGV4dCxcbiAgICAgICAgICB9LFxuICAgICAgICBdKSxcbiAgICAgIF0sXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgWS5qcyBwYXRoIGZyb20gdGhlIGJvdW5kIG9iamVjdCB0byB0aGUgWWpzVGV4dE1vZGVsIGluc3RhbmNlLlxuICAgKi9cbiAgQGNvbXB1dGVkXG4gIHByaXZhdGUgZ2V0IF95anNPYmplY3RQYXRoKCkge1xuICAgIGNvbnN0IGN0eCA9IHlqc0JpbmRpbmdDb250ZXh0LmdldCh0aGlzKVxuICAgIGlmIChjdHg/LmJvdW5kT2JqZWN0ID09IG51bGwpIHtcbiAgICAgIHRocm93IGZhaWx1cmUoXG4gICAgICAgIFwidGhlIFlqc1RleHRNb2RlbCBpbnN0YW5jZSBtdXN0IGJlIHBhcnQgb2YgYSBib3VuZCBvYmplY3QgYmVmb3JlIGl0IGNhbiBiZSBhY2Nlc3NlZFwiXG4gICAgICApXG4gICAgfVxuXG4gICAgY29uc3QgcGF0aCA9IGdldFBhcmVudFRvQ2hpbGRQYXRoKGN0eC5ib3VuZE9iamVjdCwgdGhpcylcbiAgICBpZiAoIXBhdGgpIHtcbiAgICAgIHRocm93IGZhaWx1cmUoXCJhIHBhdGggZnJvbSB0aGUgYm91bmQgb2JqZWN0IHRvIHRoZSBZanNUZXh0TW9kZWwgaW5zdGFuY2UgaXMgbm90IGF2YWlsYWJsZVwiKVxuICAgIH1cblxuICAgIHJldHVybiBwYXRoXG4gIH1cblxuICAvKipcbiAgICogVGhlIFlqcy5UZXh0IG9iamVjdCBwcmVzZW50IGF0IHRoaXMgbW9ieC1rZXlzdG9uZSBub2RlJ3MgcGF0aC5cbiAgICovXG4gIEBjb21wdXRlZFxuICBwcml2YXRlIGdldCBfeWpzT2JqZWN0QXRQYXRoKCk6IHVua25vd24ge1xuICAgIGNvbnN0IHBhdGggPSB0aGlzLl95anNPYmplY3RQYXRoXG5cbiAgICBjb25zdCBjdHggPSB5anNCaW5kaW5nQ29udGV4dC5nZXQodGhpcykhXG5cbiAgICByZXR1cm4gcmVzb2x2ZVlqc1BhdGgoY3R4Lnlqc09iamVjdCwgcGF0aClcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgWWpzLlRleHQgb2JqZWN0IHJlcHJlc2VudGVkIGJ5IHRoaXMgbW9ieC1rZXlzdG9uZSBub2RlLlxuICAgKi9cbiAgQGNvbXB1dGVkXG4gIGdldCB5anNUZXh0KCk6IFkuVGV4dCB7XG4gICAgY29uc3QgeWpzT2JqZWN0ID0gdGhpcy5feWpzT2JqZWN0QXRQYXRoXG5cbiAgICBpZiAoISh5anNPYmplY3QgaW5zdGFuY2VvZiBZLlRleHQpKSB7XG4gICAgICB0aHJvdyBmYWlsdXJlKGBZLlRleHQgd2FzIGV4cGVjdGVkIGF0IHBhdGggJHtKU09OLnN0cmluZ2lmeSh0aGlzLl95anNPYmplY3RQYXRoKX1gKVxuICAgIH1cblxuICAgIHJldHVybiB5anNPYmplY3RcbiAgfVxuXG4gIC8qKlxuICAgKiBBdG9tIHRoYXQgZ2V0cyBjaGFuZ2VkIHdoZW4gdGhlIGFzc29jaWF0ZWQgWS5qcyB0ZXh0IGNoYW5nZXMuXG4gICAqL1xuICB5anNUZXh0Q2hhbmdlZEF0b20gPSBjcmVhdGVBdG9tKFwieWpzVGV4dENoYW5nZWRBdG9tXCIpXG5cbiAgLyoqXG4gICAqIFRoZSB0ZXh0IHZhbHVlIG9mIHRoZSBZanMuVGV4dCBvYmplY3QuXG4gICAqIFNob3J0Y3V0IGZvciBgeWpzVGV4dC50b1N0cmluZygpYCwgYnV0IGNvbXB1dGVkLlxuICAgKi9cbiAgQGNvbXB1dGVkXG4gIGdldCB0ZXh0KCk6IHN0cmluZyB7XG4gICAgdGhpcy55anNUZXh0Q2hhbmdlZEF0b20ucmVwb3J0T2JzZXJ2ZWQoKVxuXG4gICAgY29uc3QgY3R4ID0geWpzQmluZGluZ0NvbnRleHQuZ2V0KHRoaXMpXG4gICAgaWYgKGN0eD8uYm91bmRPYmplY3QgIT0gbnVsbCkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgeWpzVGV4dFN0cmluZyA9IHRoaXMueWpzVGV4dC50b1N0cmluZygpXG4gICAgICAgIC8vIGlmIHRoZSB5anNUZXh0IGlzIGRldGFjaGVkLCB0b1N0cmluZygpIHJldHVybnMgYW4gZW1wdHkgc3RyaW5nXG4gICAgICAgIC8vIGluIHRoYXQgY2FzZSB3ZSBzaG91bGQgdXNlIHRoZSBkZWx0YUxpc3QgYXMgYSBmYWxsYmFja1xuICAgICAgICBpZiAoeWpzVGV4dFN0cmluZyAhPT0gXCJcIiB8fCB0aGlzLmRlbHRhTGlzdC5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICByZXR1cm4geWpzVGV4dFN0cmluZ1xuICAgICAgICB9XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLy8gZmFsbCBiYWNrXG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gZmFsbCBiYWNrIHRvIGRlbHRhTGlzdFxuICAgIHJldHVybiB0aGlzLmRlbHRhTGlzdFRvVGV4dCgpXG4gIH1cblxuICBwcml2YXRlIGRlbHRhTGlzdFRvVGV4dCgpOiBzdHJpbmcge1xuICAgIGNvbnN0IGRvYyA9IG5ldyBZLkRvYygpXG4gICAgY29uc3QgdGV4dCA9IGRvYy5nZXRUZXh0KClcbiAgICB0aGlzLmRlbHRhTGlzdC5mb3JFYWNoKChkKSA9PiB7XG4gICAgICB0ZXh0LmFwcGx5RGVsdGEoZC5kYXRhKVxuICAgIH0pXG4gICAgcmV0dXJuIHRleHQudG9TdHJpbmcoKVxuICB9XG5cbiAgcHJvdGVjdGVkIG9uSW5pdCgpIHtcbiAgICBjb25zdCBzaG91bGRSZXBsaWNhdGVUb1lqcyA9IChjdHg6IFlqc0JpbmRpbmdDb250ZXh0IHwgdW5kZWZpbmVkKTogY3R4IGlzIFlqc0JpbmRpbmdDb250ZXh0ID0+IHtcbiAgICAgIHJldHVybiAhIWN0eCAmJiAhIWN0eC5ib3VuZE9iamVjdCAmJiAhY3R4LmlzQXBwbHlpbmdZanNDaGFuZ2VzVG9Nb2J4S2V5c3RvbmVcbiAgICB9XG5cbiAgICBsZXQgcmVhcHBseURlbHRhc1RvWWpzVGV4dCA9IGZhbHNlXG4gICAgY29uc3QgbmV3RGVsdGFzOiBGcm96ZW48dW5rbm93bltdPltdID0gW11cblxuICAgIGxldCBkaXNwb3NlT2JzZXJ2ZURlbHRhTGlzdDogKCgpID0+IHZvaWQpIHwgdW5kZWZpbmVkXG5cbiAgICBjb25zdCBkaXNwb3NlUmVhY3Rpb25Ub0RlbHRhTGlzdFJlZkNoYW5nZSA9IHJlYWN0aW9uKFxuICAgICAgKCkgPT4gdGhpcy4kLmRlbHRhTGlzdCxcbiAgICAgIChkZWx0YUxpc3QpID0+IHtcbiAgICAgICAgZGlzcG9zZU9ic2VydmVEZWx0YUxpc3Q/LigpXG4gICAgICAgIGRpc3Bvc2VPYnNlcnZlRGVsdGFMaXN0ID0gdW5kZWZpbmVkXG5cbiAgICAgICAgZGlzcG9zZU9ic2VydmVEZWx0YUxpc3QgPSBvYnNlcnZlKGRlbHRhTGlzdCwgKGNoYW5nZSkgPT4ge1xuICAgICAgICAgIGlmIChyZWFwcGx5RGVsdGFzVG9ZanNUZXh0KSB7XG4gICAgICAgICAgICAvLyBhbHJlYWR5IGdvbm5hIHJlcGxhY2UgdGhlbSBhbGxcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoIXNob3VsZFJlcGxpY2F0ZVRvWWpzKHlqc0JpbmRpbmdDb250ZXh0LmdldCh0aGlzKSkpIHtcbiAgICAgICAgICAgIC8vIHlqcyB0ZXh0IGlzIGFscmVhZHkgdXAgdG8gZGF0ZSB3aXRoIHRoZXNlIGNoYW5nZXNcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgIH1cblxuICAgICAgICAgIGlmIChcbiAgICAgICAgICAgIGNoYW5nZS50eXBlID09PSBcInNwbGljZVwiICYmXG4gICAgICAgICAgICBjaGFuZ2UucmVtb3ZlZENvdW50ID09PSAwICYmXG4gICAgICAgICAgICBjaGFuZ2UuYWRkZWRDb3VudCA+IDAgJiZcbiAgICAgICAgICAgIGNoYW5nZS5pbmRleCA9PT0gdGhpcy5kZWx0YUxpc3QubGVuZ3RoXG4gICAgICAgICAgKSB7XG4gICAgICAgICAgICAvLyBvcHRpbWl6YXRpb24sIGp1c3QgYWRkaW5nIG5ldyBvbmVzIHRvIHRoZSBlbmRcbiAgICAgICAgICAgIG5ld0RlbHRhcy5wdXNoKC4uLmNoYW5nZS5hZGRlZClcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gYW55IG90aGVyIGNoYW5nZSwgd2UgbmVlZCB0byByZWFwcGx5IGFsbCBkZWx0YXNcbiAgICAgICAgICAgIHJlYXBwbHlEZWx0YXNUb1lqc1RleHQgPSB0cnVlXG4gICAgICAgICAgfVxuICAgICAgICB9KVxuICAgICAgfSxcbiAgICAgIHsgZmlyZUltbWVkaWF0ZWx5OiB0cnVlIH1cbiAgICApXG5cbiAgICBjb25zdCBkaXNwb3NlT25TbmFwc2hvdCA9IG9uU25hcHNob3QodGhpcywgKCkgPT4ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgaWYgKHJlYXBwbHlEZWx0YXNUb1lqc1RleHQpIHtcbiAgICAgICAgICBjb25zdCBjdHggPSB5anNCaW5kaW5nQ29udGV4dC5nZXQodGhpcylcblxuICAgICAgICAgIGlmIChzaG91bGRSZXBsaWNhdGVUb1lqcyhjdHgpKSB7XG4gICAgICAgICAgICBjb25zdCB7IHlqc1RleHQgfSA9IHRoaXNcbiAgICAgICAgICAgIGlmIChpc1lqc1ZhbHVlRGVsZXRlZCh5anNUZXh0KSkge1xuICAgICAgICAgICAgICB0aHJvdyBmYWlsdXJlKFwiY2Fubm90IHJlYXBwbHkgZGVsdGFzIHRvIGRlbGV0ZWQgWWpzLlRleHRcIilcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY3R4Lnlqc0RvYy50cmFuc2FjdCgoKSA9PiB7XG4gICAgICAgICAgICAgIC8vIGRpZG4ndCBmaW5kIGEgYmV0dGVyIHdheSB0aGFuIHRoaXMgdG8gcmVhcHBseSBhbGwgZGVsdGFzXG4gICAgICAgICAgICAgIC8vIHdpdGhvdXQgaGF2aW5nIHRvIHJlLWNyZWF0ZSB0aGUgWS5UZXh0IG9iamVjdFxuICAgICAgICAgICAgICBpZiAoeWpzVGV4dC5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgeWpzVGV4dC5kZWxldGUoMCwgeWpzVGV4dC5sZW5ndGgpXG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICB0aGlzLmRlbHRhTGlzdC5mb3JFYWNoKChmcm96ZW5EZWx0YXMpID0+IHtcbiAgICAgICAgICAgICAgICB5anNUZXh0LmFwcGx5RGVsdGEoZnJvemVuRGVsdGFzLmRhdGEpXG4gICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB9LCBjdHgueWpzT3JpZ2luKVxuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChuZXdEZWx0YXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGNvbnN0IGN0eCA9IHlqc0JpbmRpbmdDb250ZXh0LmdldCh0aGlzKVxuXG4gICAgICAgICAgaWYgKHNob3VsZFJlcGxpY2F0ZVRvWWpzKGN0eCkpIHtcbiAgICAgICAgICAgIGNvbnN0IHsgeWpzVGV4dCB9ID0gdGhpc1xuICAgICAgICAgICAgaWYgKGlzWWpzVmFsdWVEZWxldGVkKHlqc1RleHQpKSB7XG4gICAgICAgICAgICAgIHRocm93IGZhaWx1cmUoXCJjYW5ub3QgcmVhcHBseSBkZWx0YXMgdG8gZGVsZXRlZCBZanMuVGV4dFwiKVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjdHgueWpzRG9jLnRyYW5zYWN0KCgpID0+IHtcbiAgICAgICAgICAgICAgbmV3RGVsdGFzLmZvckVhY2goKGZyb3plbkRlbHRhcykgPT4ge1xuICAgICAgICAgICAgICAgIHlqc1RleHQuYXBwbHlEZWx0YShmcm96ZW5EZWx0YXMuZGF0YSlcbiAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH0sIGN0eC55anNPcmlnaW4pXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGZpbmFsbHkge1xuICAgICAgICByZWFwcGx5RGVsdGFzVG9ZanNUZXh0ID0gZmFsc2VcbiAgICAgICAgbmV3RGVsdGFzLmxlbmd0aCA9IDBcbiAgICAgIH1cbiAgICB9KVxuXG4gICAgY29uc3QgZGlwb3NlWWpzVGV4dENoYW5nZWRBdG9tID0gaG9va1lqc1RleHRDaGFuZ2VkQXRvbShcbiAgICAgICgpID0+IHRoaXMueWpzVGV4dCxcbiAgICAgIHRoaXMueWpzVGV4dENoYW5nZWRBdG9tXG4gICAgKVxuXG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgIGRpc3Bvc2VPblNuYXBzaG90KClcbiAgICAgIGRpc3Bvc2VSZWFjdGlvblRvRGVsdGFMaXN0UmVmQ2hhbmdlKClcbiAgICAgIGRpc3Bvc2VPYnNlcnZlRGVsdGFMaXN0Py4oKVxuICAgICAgZGlzcG9zZU9ic2VydmVEZWx0YUxpc3QgPSB1bmRlZmluZWRcblxuICAgICAgZGlwb3NlWWpzVGV4dENoYW5nZWRBdG9tKClcbiAgICB9XG4gIH1cbn1cblxuLy8gd2UgdXNlIHRoaXMgdHJpY2sganVzdCB0byBhdm9pZCBhIGJhYmVsIGJ1ZyB0aGF0IGNhdXNlcyBjbGFzc2VzIHVzZWQgaW5zaWRlIGNsYXNzZXMgbm90IHRvIGJlIG92ZXJyaWRlblxuLy8gYnkgdGhlIGRlY29yYXRvclxuY29uc3QgRGVjb3JhdGVkWWpzVGV4dE1vZGVsID0gWWpzVGV4dE1vZGVsXG5cbmZ1bmN0aW9uIGhvb2tZanNUZXh0Q2hhbmdlZEF0b20oZ2V0WWpzVGV4dDogKCkgPT4gWS5UZXh0LCB0ZXh0Q2hhbmdlZEF0b206IElBdG9tKSB7XG4gIGxldCBkaXNwb3NlT2JzZXJ2ZVlqc1RleHQ6ICgoKSA9PiB2b2lkKSB8IHVuZGVmaW5lZFxuXG4gIGNvbnN0IG9ic2VydmVGbiA9ICgpID0+IHtcbiAgICB0ZXh0Q2hhbmdlZEF0b20ucmVwb3J0Q2hhbmdlZCgpXG4gIH1cblxuICBjb25zdCBkaXNwb3NlUmVhY3Rpb25Ub1lUZXh0Q2hhbmdlID0gcmVhY3Rpb24oXG4gICAgKCkgPT4ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgeWpzVGV4dCA9IGdldFlqc1RleHQoKVxuICAgICAgICByZXR1cm4gaXNZanNWYWx1ZURlbGV0ZWQoeWpzVGV4dCkgPyB1bmRlZmluZWQgOiB5anNUZXh0XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZFxuICAgICAgfVxuICAgIH0sXG4gICAgKHlqc1RleHQpID0+IHtcbiAgICAgIGRpc3Bvc2VPYnNlcnZlWWpzVGV4dD8uKClcbiAgICAgIGRpc3Bvc2VPYnNlcnZlWWpzVGV4dCA9IHVuZGVmaW5lZFxuXG4gICAgICBpZiAoeWpzVGV4dCkge1xuICAgICAgICB5anNUZXh0Lm9ic2VydmUob2JzZXJ2ZUZuKVxuXG4gICAgICAgIGRpc3Bvc2VPYnNlcnZlWWpzVGV4dCA9ICgpID0+IHtcbiAgICAgICAgICB5anNUZXh0LnVub2JzZXJ2ZShvYnNlcnZlRm4pXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgdGV4dENoYW5nZWRBdG9tLnJlcG9ydENoYW5nZWQoKVxuICAgIH0sXG4gICAge1xuICAgICAgZmlyZUltbWVkaWF0ZWx5OiB0cnVlLFxuICAgIH1cbiAgKVxuXG4gIHJldHVybiAoKSA9PiB7XG4gICAgZGlzcG9zZVJlYWN0aW9uVG9ZVGV4dENoYW5nZSgpXG4gICAgZGlzcG9zZU9ic2VydmVZanNUZXh0Py4oKVxuICAgIGRpc3Bvc2VPYnNlcnZlWWpzVGV4dCA9IHVuZGVmaW5lZFxuICB9XG59XG4iLCJpbXBvcnQgKiBhcyBZIGZyb20gXCJ5anNcIlxuXG4vKipcbiAqIFdlYWtNYXAgdGhhdCB0cmFja3Mgd2hpY2ggc25hcHNob3QgZWFjaCBZLmpzIGNvbnRhaW5lciB3YXMgbGFzdCBzeW5jZWQgZnJvbS5cbiAqIFRoaXMgaXMgdXNlZCBkdXJpbmcgcmVjb25jaWxpYXRpb24gdG8gc2tpcCBjb250YWluZXJzIHRoYXQgYXJlIGFscmVhZHkgdXAtdG8tZGF0ZS5cbiAqXG4gKiBUaGUga2V5IGlzIHRoZSBZLmpzIGNvbnRhaW5lciAoWS5NYXAgb3IgWS5BcnJheSkuXG4gKiBUaGUgdmFsdWUgaXMgdGhlIHNuYXBzaG90IChwbGFpbiBvYmplY3Qgb3IgYXJyYXkpIHRoYXQgd2FzIGxhc3Qgc3luY2VkIHRvIGl0LlxuICovXG5leHBvcnQgY29uc3QgeWpzQ29udGFpbmVyVG9TbmFwc2hvdCA9IG5ldyBXZWFrTWFwPFkuTWFwPGFueT4gfCBZLkFycmF5PGFueT4sIHVua25vd24+KClcblxuLyoqXG4gKiBVcGRhdGVzIHRoZSBzbmFwc2hvdCB0cmFja2luZyBmb3IgYSBZLmpzIGNvbnRhaW5lci5cbiAqIENhbGwgdGhpcyBhZnRlciBzeW5jaW5nIGEgc25hcHNob3QgdG8gYSBZLmpzIGNvbnRhaW5lci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNldFlqc0NvbnRhaW5lclNuYXBzaG90KFxuICBjb250YWluZXI6IFkuTWFwPGFueT4gfCBZLkFycmF5PGFueT4sXG4gIHNuYXBzaG90OiB1bmtub3duXG4pOiB2b2lkIHtcbiAgeWpzQ29udGFpbmVyVG9TbmFwc2hvdC5zZXQoY29udGFpbmVyLCBzbmFwc2hvdClcbn1cblxuLyoqXG4gKiBHZXRzIHRoZSBsYXN0IHN5bmNlZCBzbmFwc2hvdCBmb3IgYSBZLmpzIGNvbnRhaW5lci5cbiAqIFJldHVybnMgdW5kZWZpbmVkIGlmIHRoZSBjb250YWluZXIgaGFzIG5ldmVyIGJlZW4gc3luY2VkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0WWpzQ29udGFpbmVyU25hcHNob3QoY29udGFpbmVyOiBZLk1hcDxhbnk+IHwgWS5BcnJheTxhbnk+KTogdW5rbm93biB7XG4gIHJldHVybiB5anNDb250YWluZXJUb1NuYXBzaG90LmdldChjb250YWluZXIpXG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGEgWS5qcyBjb250YWluZXIgaXMgdXAtdG8tZGF0ZSB3aXRoIHRoZSBnaXZlbiBzbmFwc2hvdC5cbiAqIFVzZXMgcmVmZXJlbmNlIGVxdWFsaXR5IHRvIGNoZWNrIGlmIHRoZSBzbmFwc2hvdCBpcyB0aGUgc2FtZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzWWpzQ29udGFpbmVyVXBUb0RhdGUoXG4gIGNvbnRhaW5lcjogWS5NYXA8YW55PiB8IFkuQXJyYXk8YW55PixcbiAgc25hcHNob3Q6IHVua25vd25cbik6IGJvb2xlYW4ge1xuICByZXR1cm4geWpzQ29udGFpbmVyVG9TbmFwc2hvdC5nZXQoY29udGFpbmVyKSA9PT0gc25hcHNob3Rcbn1cbiIsImltcG9ydCB7IGZyb3plbktleSwgbW9kZWxUeXBlS2V5LCBTbmFwc2hvdE91dE9mIH0gZnJvbSBcIm1vYngta2V5c3RvbmVcIlxuaW1wb3J0ICogYXMgWSBmcm9tIFwieWpzXCJcbmltcG9ydCB7IFBsYWluQXJyYXksIFBsYWluT2JqZWN0LCBQbGFpblByaW1pdGl2ZSwgUGxhaW5WYWx1ZSB9IGZyb20gXCIuLi9wbGFpblR5cGVzXCJcbmltcG9ydCB7IFlqc0RhdGEgfSBmcm9tIFwiLi9jb252ZXJ0WWpzRGF0YVRvSnNvblwiXG5pbXBvcnQgeyBZanNUZXh0TW9kZWwsIHlqc1RleHRNb2RlbElkIH0gZnJvbSBcIi4vWWpzVGV4dE1vZGVsXCJcbmltcG9ydCB7IGlzWWpzQ29udGFpbmVyVXBUb0RhdGUsIHNldFlqc0NvbnRhaW5lclNuYXBzaG90IH0gZnJvbSBcIi4veWpzU25hcHNob3RUcmFja2luZ1wiXG5cbi8qKlxuICogT3B0aW9ucyBmb3IgYXBwbHlpbmcgSlNPTiBkYXRhIHRvIFkuanMgZGF0YSBzdHJ1Y3R1cmVzLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEFwcGx5SnNvblRvWWpzT3B0aW9ucyB7XG4gIC8qKlxuICAgKiBUaGUgbW9kZSB0byB1c2Ugd2hlbiBhcHBseWluZyBKU09OIGRhdGEgdG8gWS5qcyBkYXRhIHN0cnVjdHVyZXMuXG4gICAqIC0gYGFkZGA6IENyZWF0ZXMgbmV3IFkuanMgY29udGFpbmVycyBmb3Igb2JqZWN0cy9hcnJheXMgKGRlZmF1bHQsIGJhY2t3YXJkcyBjb21wYXRpYmxlKVxuICAgKiAtIGBtZXJnZWA6IFJlY3Vyc2l2ZWx5IG1lcmdlcyB2YWx1ZXMsIHByZXNlcnZpbmcgZXhpc3RpbmcgY29udGFpbmVyIHJlZmVyZW5jZXMgd2hlcmUgcG9zc2libGVcbiAgICovXG4gIG1vZGU/OiBcImFkZFwiIHwgXCJtZXJnZVwiXG59XG5cbmZ1bmN0aW9uIGlzUGxhaW5QcmltaXRpdmUodjogUGxhaW5WYWx1ZSk6IHYgaXMgUGxhaW5QcmltaXRpdmUge1xuICBjb25zdCB0ID0gdHlwZW9mIHZcbiAgcmV0dXJuIHQgPT09IFwic3RyaW5nXCIgfHwgdCA9PT0gXCJudW1iZXJcIiB8fCB0ID09PSBcImJvb2xlYW5cIiB8fCB2ID09PSBudWxsIHx8IHYgPT09IHVuZGVmaW5lZFxufVxuXG5mdW5jdGlvbiBpc1BsYWluQXJyYXkodjogUGxhaW5WYWx1ZSk6IHYgaXMgUGxhaW5BcnJheSB7XG4gIHJldHVybiBBcnJheS5pc0FycmF5KHYpXG59XG5cbmZ1bmN0aW9uIGlzUGxhaW5PYmplY3QodjogUGxhaW5WYWx1ZSk6IHYgaXMgUGxhaW5PYmplY3Qge1xuICByZXR1cm4gdHlwZW9mIHYgPT09IFwib2JqZWN0XCIgJiYgdiAhPT0gbnVsbCAmJiAhQXJyYXkuaXNBcnJheSh2KVxufVxuXG4vKipcbiAqIENvbnZlcnRzIGEgcGxhaW4gdmFsdWUgdG8gYSBZLmpzIGRhdGEgc3RydWN0dXJlLlxuICogT2JqZWN0cyBhcmUgY29udmVydGVkIHRvIFkuTWFwcywgYXJyYXlzIHRvIFkuQXJyYXlzLCBwcmltaXRpdmVzIGFyZSB1bnRvdWNoZWQuXG4gKiBGcm96ZW4gdmFsdWVzIGFyZSBhIHNwZWNpYWwgY2FzZSBhbmQgdGhleSBhcmUga2VwdCBhcyBpbW11dGFibGUgcGxhaW4gdmFsdWVzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY29udmVydEpzb25Ub1lqc0RhdGEodjogUGxhaW5WYWx1ZSk6IFlqc0RhdGEge1xuICBpZiAoaXNQbGFpblByaW1pdGl2ZSh2KSkge1xuICAgIHJldHVybiB2XG4gIH1cblxuICBpZiAoaXNQbGFpbkFycmF5KHYpKSB7XG4gICAgY29uc3QgYXJyID0gbmV3IFkuQXJyYXkoKVxuICAgIGFwcGx5SnNvbkFycmF5VG9ZQXJyYXkoYXJyLCB2KVxuICAgIHJldHVybiBhcnJcbiAgfVxuXG4gIGlmIChpc1BsYWluT2JqZWN0KHYpKSB7XG4gICAgaWYgKHZbZnJvemVuS2V5XSA9PT0gdHJ1ZSkge1xuICAgICAgLy8gZnJvemVuIHZhbHVlLCBzYXZlIGFzIGltbXV0YWJsZSBvYmplY3RcbiAgICAgIHJldHVybiB2XG4gICAgfVxuXG4gICAgaWYgKHZbbW9kZWxUeXBlS2V5XSA9PT0geWpzVGV4dE1vZGVsSWQpIHtcbiAgICAgIGNvbnN0IHRleHQgPSBuZXcgWS5UZXh0KClcbiAgICAgIGNvbnN0IHlqc1RleHRNb2RlbCA9IHYgYXMgdW5rbm93biBhcyBTbmFwc2hvdE91dE9mPFlqc1RleHRNb2RlbD5cbiAgICAgIHlqc1RleHRNb2RlbC5kZWx0YUxpc3QuZm9yRWFjaCgoZnJvemVuRGVsdGFzKSA9PiB7XG4gICAgICAgIHRleHQuYXBwbHlEZWx0YShmcm96ZW5EZWx0YXMuZGF0YSlcbiAgICAgIH0pXG4gICAgICByZXR1cm4gdGV4dFxuICAgIH1cblxuICAgIGNvbnN0IG1hcCA9IG5ldyBZLk1hcCgpXG4gICAgYXBwbHlKc29uT2JqZWN0VG9ZTWFwKG1hcCwgdilcbiAgICByZXR1cm4gbWFwXG4gIH1cblxuICB0aHJvdyBuZXcgRXJyb3IoYHVuc3VwcG9ydGVkIHZhbHVlIHR5cGU6ICR7dn1gKVxufVxuXG4vKipcbiAqIEFwcGxpZXMgYSBKU09OIGFycmF5IHRvIGEgWS5BcnJheSwgdXNpbmcgdGhlIGNvbnZlcnRKc29uVG9ZanNEYXRhIHRvIGNvbnZlcnQgdGhlIHZhbHVlcy5cbiAqXG4gKiBAcGFyYW0gZGVzdCBUaGUgZGVzdGluYXRpb24gWS5BcnJheS5cbiAqIEBwYXJhbSBzb3VyY2UgVGhlIHNvdXJjZSBKU09OIGFycmF5LlxuICogQHBhcmFtIG9wdGlvbnMgT3B0aW9ucyBmb3IgYXBwbHlpbmcgdGhlIEpTT04gZGF0YS5cbiAqL1xuZXhwb3J0IGNvbnN0IGFwcGx5SnNvbkFycmF5VG9ZQXJyYXkgPSAoXG4gIGRlc3Q6IFkuQXJyYXk8YW55PixcbiAgc291cmNlOiBQbGFpbkFycmF5LFxuICBvcHRpb25zOiBBcHBseUpzb25Ub1lqc09wdGlvbnMgPSB7fVxuKSA9PiB7XG4gIGNvbnN0IHsgbW9kZSA9IFwiYWRkXCIgfSA9IG9wdGlvbnNcblxuICAvLyBJbiBtZXJnZSBtb2RlLCBjaGVjayBpZiB0aGUgY29udGFpbmVyIGlzIGFscmVhZHkgdXAtdG8tZGF0ZSB3aXRoIHRoaXMgc25hcHNob3RcbiAgaWYgKG1vZGUgPT09IFwibWVyZ2VcIiAmJiBpc1lqc0NvbnRhaW5lclVwVG9EYXRlKGRlc3QsIHNvdXJjZSkpIHtcbiAgICByZXR1cm5cbiAgfVxuXG4gIGNvbnN0IHNyY0xlbiA9IHNvdXJjZS5sZW5ndGhcblxuICBpZiAobW9kZSA9PT0gXCJhZGRcIikge1xuICAgIC8vIEFkZCBtb2RlOiBqdXN0IHB1c2ggYWxsIGl0ZW1zIHRvIHRoZSBlbmRcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHNyY0xlbjsgaSsrKSB7XG4gICAgICBkZXN0LnB1c2goW2NvbnZlcnRKc29uVG9ZanNEYXRhKHNvdXJjZVtpXSldKVxuICAgIH1cbiAgICByZXR1cm5cbiAgfVxuXG4gIC8vIE1lcmdlIG1vZGU6IHJlY3Vyc2l2ZWx5IG1lcmdlIHZhbHVlcywgcHJlc2VydmluZyBleGlzdGluZyBjb250YWluZXIgcmVmZXJlbmNlc1xuICBjb25zdCBkZXN0TGVuID0gZGVzdC5sZW5ndGhcblxuICAvLyBSZW1vdmUgZXh0cmEgaXRlbXMgZnJvbSB0aGUgZW5kXG4gIGlmIChkZXN0TGVuID4gc3JjTGVuKSB7XG4gICAgZGVzdC5kZWxldGUoc3JjTGVuLCBkZXN0TGVuIC0gc3JjTGVuKVxuICB9XG5cbiAgLy8gVXBkYXRlIGV4aXN0aW5nIGl0ZW1zXG4gIGNvbnN0IG1pbkxlbiA9IE1hdGgubWluKGRlc3RMZW4sIHNyY0xlbilcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBtaW5MZW47IGkrKykge1xuICAgIGNvbnN0IHNyY0l0ZW0gPSBzb3VyY2VbaV1cbiAgICBjb25zdCBkZXN0SXRlbSA9IGRlc3QuZ2V0KGkpXG5cbiAgICAvLyBJZiBib3RoIGFyZSBvYmplY3RzLCBtZXJnZSByZWN1cnNpdmVseVxuICAgIGlmIChpc1BsYWluT2JqZWN0KHNyY0l0ZW0pICYmIGRlc3RJdGVtIGluc3RhbmNlb2YgWS5NYXApIHtcbiAgICAgIGFwcGx5SnNvbk9iamVjdFRvWU1hcChkZXN0SXRlbSwgc3JjSXRlbSwgb3B0aW9ucylcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuXG4gICAgLy8gSWYgYm90aCBhcmUgYXJyYXlzLCBtZXJnZSByZWN1cnNpdmVseVxuICAgIGlmIChpc1BsYWluQXJyYXkoc3JjSXRlbSkgJiYgZGVzdEl0ZW0gaW5zdGFuY2VvZiBZLkFycmF5KSB7XG4gICAgICBhcHBseUpzb25BcnJheVRvWUFycmF5KGRlc3RJdGVtLCBzcmNJdGVtLCBvcHRpb25zKVxuICAgICAgY29udGludWVcbiAgICB9XG5cbiAgICAvLyBTa2lwIGlmIHByaW1pdGl2ZSB2YWx1ZSBpcyB1bmNoYW5nZWQgKG9wdGltaXphdGlvbilcbiAgICBpZiAoaXNQbGFpblByaW1pdGl2ZShzcmNJdGVtKSAmJiBkZXN0SXRlbSA9PT0gc3JjSXRlbSkge1xuICAgICAgY29udGludWVcbiAgICB9XG5cbiAgICAvLyBPdGhlcndpc2UsIHJlcGxhY2UgdGhlIGl0ZW1cbiAgICBkZXN0LmRlbGV0ZShpLCAxKVxuICAgIGRlc3QuaW5zZXJ0KGksIFtjb252ZXJ0SnNvblRvWWpzRGF0YShzcmNJdGVtKV0pXG4gIH1cblxuICAvLyBBZGQgbmV3IGl0ZW1zIGF0IHRoZSBlbmRcbiAgZm9yIChsZXQgaSA9IGRlc3RMZW47IGkgPCBzcmNMZW47IGkrKykge1xuICAgIGRlc3QucHVzaChbY29udmVydEpzb25Ub1lqc0RhdGEoc291cmNlW2ldKV0pXG4gIH1cblxuICAvLyBVcGRhdGUgc25hcHNob3QgdHJhY2tpbmcgYWZ0ZXIgc3VjY2Vzc2Z1bCBtZXJnZVxuICBzZXRZanNDb250YWluZXJTbmFwc2hvdChkZXN0LCBzb3VyY2UpXG59XG5cbi8qKlxuICogQXBwbGllcyBhIEpTT04gb2JqZWN0IHRvIGEgWS5NYXAsIHVzaW5nIHRoZSBjb252ZXJ0SnNvblRvWWpzRGF0YSB0byBjb252ZXJ0IHRoZSB2YWx1ZXMuXG4gKlxuICogQHBhcmFtIGRlc3QgVGhlIGRlc3RpbmF0aW9uIFkuTWFwLlxuICogQHBhcmFtIHNvdXJjZSBUaGUgc291cmNlIEpTT04gb2JqZWN0LlxuICogQHBhcmFtIG9wdGlvbnMgT3B0aW9ucyBmb3IgYXBwbHlpbmcgdGhlIEpTT04gZGF0YS5cbiAqL1xuZXhwb3J0IGNvbnN0IGFwcGx5SnNvbk9iamVjdFRvWU1hcCA9IChcbiAgZGVzdDogWS5NYXA8YW55PixcbiAgc291cmNlOiBQbGFpbk9iamVjdCxcbiAgb3B0aW9uczogQXBwbHlKc29uVG9ZanNPcHRpb25zID0ge31cbikgPT4ge1xuICBjb25zdCB7IG1vZGUgPSBcImFkZFwiIH0gPSBvcHRpb25zXG5cbiAgLy8gSW4gbWVyZ2UgbW9kZSwgY2hlY2sgaWYgdGhlIGNvbnRhaW5lciBpcyBhbHJlYWR5IHVwLXRvLWRhdGUgd2l0aCB0aGlzIHNuYXBzaG90XG4gIGlmIChtb2RlID09PSBcIm1lcmdlXCIgJiYgaXNZanNDb250YWluZXJVcFRvRGF0ZShkZXN0LCBzb3VyY2UpKSB7XG4gICAgcmV0dXJuXG4gIH1cblxuICBpZiAobW9kZSA9PT0gXCJhZGRcIikge1xuICAgIC8vIEFkZCBtb2RlOiBqdXN0IHNldCBhbGwgdmFsdWVzXG4gICAgZm9yIChjb25zdCBrIG9mIE9iamVjdC5rZXlzKHNvdXJjZSkpIHtcbiAgICAgIGNvbnN0IHYgPSBzb3VyY2Vba11cbiAgICAgIGlmICh2ICE9PSB1bmRlZmluZWQpIHtc