UNPKG

mobx-keystone-yjs

Version:

Yjs bindings for mobx-keystone

759 lines (758 loc) 103 kB
(function(global, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("mobx"), require("mobx-keystone"), require("yjs")) : typeof define === "function" && define.amd ? define(["exports", "mobx", "mobx-keystone", "yjs"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["mobx-keystone-yjs"] = {}, global.mobx, global["mobx-keystone"], global.yjs)); })(this, (function(exports2, mobx, mobxKeystone, Y) { "use strict";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); function _interopNamespaceDefault(e) { const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } }); if (e) { for (const k in e) { if (k !== "default") { const d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: () => e[k] }); } } } n.default = e; return Object.freeze(n); } const Y__namespace = /* @__PURE__ */ _interopNamespaceDefault(Y); 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 = mobx.createAtom(`yjsCollectionAtom`); yjsCollectionAtoms.set(yjsCollection, atom); } return atom; }; function isYjsValueDeleted(yjsValue) { var _a, _b; if (yjsValue instanceof Y__namespace.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__namespace.Text) { return currentYjsObject; } if (currentYjsObject instanceof Y__namespace.Map) { getOrCreateYjsCollectionAtom(currentYjsObject).reportObserved(); const key = String(pathPart); currentYjsObject = currentYjsObject.get(key); } else if (currentYjsObject instanceof Y__namespace.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 = mobxKeystone.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 = mobxKeystone.types.array(mobxKeystone.types.frozen(mobxKeystone.types.unchecked())); const yjsTextModelId = "mobx-keystone-yjs/YjsTextModel"; exports2.YjsTextModel = class YjsTextModel extends mobxKeystone.Model({ deltaList: mobxKeystone.tProp(deltaListType, () => []) }) { constructor() { super(...arguments); /** * Atom that gets changed when the associated Y.js text changes. */ __publicField(this, "yjsTextChangedAtom", mobx.createAtom("yjsTextChangedAtom")); } /** * Helper function to create a YjsTextModel instance with a simple text. */ static withText(text) { return new DecoratedYjsTextModel({ deltaList: [ mobxKeystone.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 = mobxKeystone.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__namespace.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__namespace.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 = mobx.reaction( () => this.$.deltaList, (deltaList) => { disposeObserveDeltaList == null ? void 0 : disposeObserveDeltaList(); disposeObserveDeltaList = void 0; disposeObserveDeltaList = mobx.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 = mobxKeystone.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([ mobx.computed ], exports2.YjsTextModel.prototype, "_yjsObjectPath", 1); __decorateClass([ mobx.computed ], exports2.YjsTextModel.prototype, "_yjsObjectAtPath", 1); __decorateClass([ mobx.computed ], exports2.YjsTextModel.prototype, "yjsText", 1); __decorateClass([ mobx.computed ], exports2.YjsTextModel.prototype, "text", 1); exports2.YjsTextModel = __decorateClass([ mobxKeystone.model(yjsTextModelId) ], exports2.YjsTextModel); const DecoratedYjsTextModel = exports2.YjsTextModel; function hookYjsTextChangedAtom(getYjsText, textChangedAtom) { let disposeObserveYjsText; const observeFn = () => { textChangedAtom.reportChanged(); }; const disposeReactionToYTextChange = mobx.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__namespace.Array(); applyJsonArrayToYArray(arr, v); return arr; } if (isPlainObject(v)) { if (v[mobxKeystone.frozenKey] === true) { return v; } if (v[mobxKeystone.modelTypeKey] === yjsTextModelId) { const text = new Y__namespace.Text(); const yjsTextModel = v; yjsTextModel.deltaList.forEach((frozenDeltas) => { text.applyDelta(frozenDeltas.data); }); return text; } const map = new Y__namespace.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__namespace.Map) { applyJsonObjectToYMap(destItem, srcItem, options); continue; } if (isPlainArray(srcItem) && destItem instanceof Y__namespace.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__namespace.Map) { applyJsonObjectToYMap(existing, v, options); continue; } if (isPlainArray(v) && existing instanceof Y__namespace.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__namespace.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__namespace.Array) { if (change.type === mobxKeystone.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 === mobxKeystone.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__namespace.Map) { if (change.type === mobxKeystone.DeepChangeType.ObjectAdd || change.type === mobxKeystone.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 === mobxKeystone.DeepChangeType.ObjectRemove) { const key = change.key; yjsContainer.delete(key); } else { throw failure(`unsupported object change type: ${change.type}`); } } else if (yjsContainer instanceof Y__namespace.Text) { return; } else { throw failure(`unsupported Yjs container type: ${yjsContainer}`); } } const convertYjsDataToJson = mobx.action((yjsData) => { if (yjsData instanceof Y__namespace.Array) { return yjsData.map((v) => convertYjsDataToJson(v)); } if (yjsData instanceof Y__namespace.Map) { const obj = {}; yjsData.forEach((v, k) => { obj[k] = convertYjsDataToJson(v); }); return obj; } if (yjsData instanceof Y__namespace.Text) { const deltas = yjsData.toDelta(); return mobxKeystone.modelSnapshotOutWithMetadata(exports2.YjsTextModel, { deltaList: deltas.length > 0 ? [{ $frozen: true, data: deltas }] : [] }); } return yjsData; }); function applyYjsEventToMobx(event, boundObject, reconciliationMap) { const path = event.path; const { value: target } = mobxKeystone.resolvePath(boundObject, path); if (!target) { throw failure(`cannot resolve path ${JSON.stringify(path)}`); } mobxKeystone.runUnprotected(() => { if (event instanceof Y__namespace.YMapEvent) { applyYMapEventToMobx(event, target, reconciliationMap); } else if (event instanceof Y__namespace.YArrayEvent) { applyYArrayEventToMobx(event, target, reconciliationMap); } else if (event instanceof Y__namespace.YTextEvent) { applyYTextEventToMobx(event, target); } }); } function processDeletedValue(val, reconciliationMap) { if (val && typeof val === "object" && mobxKeystone.isModel(val)) { const sn = mobxKeystone.getSnapshot(val); const id = mobxKeystone.getSnapshotModelId(sn); if (id) { reconciliationMap.set(id, val); } } } function reviveValue(jsonValue, reconciliationMap) { if (jsonValue === null || typeof jsonValue !== "object") { return jsonValue; } if (mobxKeystone.isFrozenSnapshot(jsonValue)) { return mobxKeystone.frozen(jsonValue.data); } if (reconciliationMap && jsonValue && typeof jsonValue === "object") { const modelId = mobxKeystone.getSnapshotModelId(jsonValue); if (modelId) { const existing = reconciliationMap.get(modelId); if (existing) { reconciliationMap.delete(modelId); return existing; } } } return mobxKeystone.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 (mobxKeystone.isModel(target)) { mobx.remove(target.$, key); } else { mobx.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(mobxKeystone.frozen(event.delta)); } } function captureChangeSnapshots(change) { if (change.type === mobxKeystone.DeepChangeType.ArraySplice && change.addedValues.length > 0) { const snapshots = change.addedValues.map((v) => mobxKeystone.isTreeNode(v) ? mobxKeystone.getSnapshot(v) : v); return { ...change, addedValues: snapshots }; } else if (change.type === mobxKeystone.DeepChangeType.ArrayUpdate) { const snapshot = mobxKeystone.isTreeNode(change.newValue) ? mobxKeystone.getSnapshot(change.newValue) : change.newValue; return { ...change, newValue: snapshot }; } else if (change.type === mobxKeystone.DeepChangeType.ObjectAdd || change.type === mobxKeystone.DeepChangeType.ObjectUpdate) { const snapshot = mobxKeystone.isTreeNode(change.newValue) ? mobxKeystone.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 = mobxKeystone.onGlobalDeepChange((_target, change) => { if (change.isInit) { hasInitChanges = true; } }); try { const result = yjsBindingContext.apply( () => mobxKeystone.fromSnapshot(mobxKeystoneType, yjsJson), bindingContext ); yjsBindingContext.set(result, { ...bindingContext, boundObject: result }); return result; } finally { disposeGlobalListener(); } }; boundObject = createBoundObject(); const observeDeepCb = mobx.action((events) => { const eventsToApply = []; events.forEach((event) => { var _a; if (event.transaction.origin !== yjsOrigin) { eventsToApply.push(event); } if (event.target instanceof Y__namespace.Map || event.target instanceof Y__namespace.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 = mobxKeystone.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 = mobxKeystone.getParentToChildPath(boundObject, target); if (pathToTarget !== void 0) { const changeWithCorrectPath = { ...change, path: [...pathToTarget, ...change.path] }; applyMobxChangeToYjsObject(changeWithCorrectPath, yjsObject); } } }, yjsOrigin); } if (yjsObject instanceof Y__namespace.Map || yjsObject instanceof Y__namespace.Array) { setYjsContainerSnapshot(yjsObject, mobxKeystone.getSnapshot(boundObject)); } } finally { applyingYjsChangesToMobxKeystone--; } } }); yjsObject.observeDeep(observeDeepCb); let pendingChanges = []; const disposeOnDeepChange = mobxKeystone.onDeepChange(boundObject, (change) => { if (applyingYjsChangesToMobxKeystone > 0) { return; } if (change.isInit) { return; } pendingChanges.push(captureChangeSnapshots(change)); }); const disposeOnSnapshot = mobxKeystone.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__namespace.Map || yjsObject instanceof Y__namespace.Array) { setYjsContainerSnapshot(yjsObject, boundObjectSnapshot); } }); const finalSnapshot = mobxKeystone.getSnapshot(boundObject); if (hasInitChanges) { yjsDoc.transact(() => { if (yjsObject instanceof Y__namespace.Map) { applyJsonObjectToYMap(yjsObject, finalSnapshot, { mode: "merge" }); } else if (yjsObject instanceof Y__namespace.Array) { applyJsonArrayToYArray(yjsObject, finalSnapshot, { mode: "merge" }); } }, yjsOrigin); } if (yjsObject instanceof Y__namespace.Map || yjsObject instanceof Y__namespace.Array) { setYjsContainerSnapshot(yjsObject, finalSnapshot); } const dispose = () => { yjsDoc.off("destroy", dispose); disposeOnDeepChange(); disposeOnSnapshot(); yjsObject.unobserveDeep(observeDeepCb); }; yjsDoc.on("destroy", dispose); return { boundObject, dispose, yjsOrigin }; } exports2.MobxKeystoneYjsError = MobxKeystoneYjsError; exports2.applyJsonArrayToYArray = applyJsonArrayToYArray; exports2.applyJsonObjectToYMap = applyJsonObjectToYMap; exports2.bindYjsToMobxKeystone = bindYjsToMobxKeystone; exports2.convertJsonToYjsData = convertJsonToYjsData; exports2.yjsBindingContext = yjsBindingContext; exports2.yjsTextModelId = yjsTextModelId; Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" }); })); //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9ieC1rZXlzdG9uZS15anMudW1kLmpzIiwic291cmNlcyI6WyIuLi9zcmMvdXRpbHMvZXJyb3IudHMiLCIuLi9zcmMvdXRpbHMvZ2V0T3JDcmVhdGVZanNDb2xsZWN0aW9uQXRvbS50cyIsIi4uL3NyYy91dGlscy9pc1lqc1ZhbHVlRGVsZXRlZC50cyIsIi4uL3NyYy9iaW5kaW5nL3Jlc29sdmVZanNQYXRoLnRzIiwiLi4vc3JjL2JpbmRpbmcveWpzQmluZGluZ0NvbnRleHQudHMiLCIuLi9zcmMvYmluZGluZy9ZanNUZXh0TW9kZWwudHMiLCIuLi9zcmMvYmluZGluZy95anNTbmFwc2hvdFRyYWNraW5nLnRzIiwiLi4vc3JjL2JpbmRpbmcvY29udmVydEpzb25Ub1lqc0RhdGEudHMiLCIuLi9zcmMvYmluZGluZy9hcHBseU1vYnhDaGFuZ2VUb1lqc09iamVjdC50cyIsIi4uL3NyYy9iaW5kaW5nL2NvbnZlcnRZanNEYXRhVG9Kc29uLnRzIiwiLi4vc3JjL2JpbmRpbmcvYXBwbHlZanNFdmVudFRvTW9ieC50cyIsIi4uL3NyYy9iaW5kaW5nL2JpbmRZanNUb01vYnhLZXlzdG9uZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcclxuICogQSBtb2J4LWtleXN0b25lLXlqcyBlcnJvci5cclxuICovXHJcbmV4cG9ydCBjbGFzcyBNb2J4S2V5c3RvbmVZanNFcnJvciBleHRlbmRzIEVycm9yIHtcclxuICBjb25zdHJ1Y3Rvcihtc2c6IHN0cmluZykge1xyXG4gICAgc3VwZXIobXNnKVxyXG5cclxuICAgIC8vIFNldCB0aGUgcHJvdG90eXBlIGV4cGxpY2l0bHkuXHJcbiAgICBPYmplY3Quc2V0UHJvdG90eXBlT2YodGhpcywgTW9ieEtleXN0b25lWWpzRXJyb3IucHJvdG90eXBlKVxyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEBpbnRlcm5hbFxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGZhaWx1cmUobXNnOiBzdHJpbmcpIHtcclxuICByZXR1cm4gbmV3IE1vYnhLZXlzdG9uZVlqc0Vycm9yKG1zZylcclxufVxyXG4iLCJpbXBvcnQgeyBjcmVhdGVBdG9tLCBJQXRvbSB9IGZyb20gXCJtb2J4XCJcbmltcG9ydCAqIGFzIFkgZnJvbSBcInlqc1wiXG5cbmNvbnN0IHlqc0NvbGxlY3Rpb25BdG9tcyA9IG5ldyBXZWFrTWFwPFkuTWFwPHVua25vd24+IHwgWS5BcnJheTx1bmtub3duPiwgSUF0b20+KClcblxuLyoqXG4gKiBAaW50ZXJuYWxcbiAqL1xuZXhwb3J0IGNvbnN0IGdldFlqc0NvbGxlY3Rpb25BdG9tID0gKFxuICB5anNDb2xsZWN0aW9uOiBZLk1hcDx1bmtub3duPiB8IFkuQXJyYXk8dW5rbm93bj5cbik6IElBdG9tIHwgdW5kZWZpbmVkID0+IHtcbiAgcmV0dXJuIHlqc0NvbGxlY3Rpb25BdG9tcy5nZXQoeWpzQ29sbGVjdGlvbilcbn1cblxuLyoqXG4gKiBAaW50ZXJuYWxcbiAqL1xuZXhwb3J0IGNvbnN0IGdldE9yQ3JlYXRlWWpzQ29sbGVjdGlvbkF0b20gPSAoXG4gIHlqc0NvbGxlY3Rpb246IFkuTWFwPHVua25vd24+IHwgWS5BcnJheTx1bmtub3duPlxuKTogSUF0b20gPT4ge1xuICBsZXQgYXRvbSA9IHlqc0NvbGxlY3Rpb25BdG9tcy5nZXQoeWpzQ29sbGVjdGlvbilcbiAgaWYgKCFhdG9tKSB7XG4gICAgYXRvbSA9IGNyZWF0ZUF0b20oYHlqc0NvbGxlY3Rpb25BdG9tYClcbiAgICB5anNDb2xsZWN0aW9uQXRvbXMuc2V0KHlqc0NvbGxlY3Rpb24sIGF0b20pXG4gIH1cbiAgcmV0dXJuIGF0b21cbn1cbiIsImltcG9ydCAqIGFzIFkgZnJvbSBcInlqc1wiXG5cbi8qKlxuICogQ2hlY2tzIGlmIGEgWS5qcyB2YWx1ZSBoYXMgYmVlbiBkZWxldGVkIG9yIGl0cyBkb2N1bWVudCBkZXN0cm95ZWQuXG4gKlxuICogQHBhcmFtIHlqc1ZhbHVlIFRoZSBZLmpzIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMgYHRydWVgIGlmIHRoZSB2YWx1ZSBpcyBkZWxldGVkIG9yIGRlc3Ryb3llZCwgYGZhbHNlYCBvdGhlcndpc2UuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc1lqc1ZhbHVlRGVsZXRlZCh5anNWYWx1ZTogdW5rbm93bik6IGJvb2xlYW4ge1xuICBpZiAoeWpzVmFsdWUgaW5zdGFuY2VvZiBZLkFic3RyYWN0VHlwZSkge1xuICAgIHJldHVybiAhISh5anNWYWx1ZSBhcyBhbnkpLl9pdGVtPy5kZWxldGVkIHx8ICEheWpzVmFsdWUuZG9jPy5pc0Rlc3Ryb3llZFxuICB9XG4gIHJldHVybiBmYWxzZVxufVxuIiwiaW1wb3J0ICogYXMgWSBmcm9tIFwieWpzXCJcbmltcG9ydCB7IGZhaWx1cmUgfSBmcm9tIFwiLi4vdXRpbHMvZXJyb3JcIlxuaW1wb3J0IHsgZ2V0T3JDcmVhdGVZanNDb2xsZWN0aW9uQXRvbSB9IGZyb20gXCIuLi91dGlscy9nZXRPckNyZWF0ZVlqc0NvbGxlY3Rpb25BdG9tXCJcblxuLyoqXG4gKiBSZXNvbHZlcyBhIHBhdGggd2l0aGluIGEgWWpzIG9iamVjdCBzdHJ1Y3R1cmUuXG4gKiBSZXR1cm5zIHRoZSBZanMgY29udGFpbmVyIGF0IHRoZSBzcGVjaWZpZWQgcGF0aC5cbiAqXG4gKiBXaGVuIGEgWS5UZXh0IGlzIGVuY291bnRlcmVkIGR1cmluZyBwYXRoIHJlc29sdXRpb24gKGVpdGhlciBhdCB0aGUgc3RhcnRcbiAqIG9yIG1pZC1wYXRoKSwgaXQgaXMgcmV0dXJuZWQgaW1tZWRpYXRlbHkgc2luY2UgWS5UZXh0IGRvZXNuJ3Qgc3VwcG9ydFxuICogbmVzdGVkIHBhdGggdHJhdmVyc2FsLlxuICpcbiAqIEBwYXJhbSB5anNPYmplY3QgVGhlIHJvb3QgWWpzIG9iamVjdFxuICogQHBhcmFtIHBhdGggQXJyYXkgb2Yga2V5cy9pbmRpY2VzIHRvIHRyYXZlcnNlXG4gKiBAcmV0dXJucyBUaGUgWWpzIGNvbnRhaW5lciBhdCB0aGUgcGF0aCwgb3IgWS5UZXh0IGlmIGVuY291bnRlcmVkIGR1cmluZyB0cmF2ZXJzYWxcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVZanNQYXRoKFxuICB5anNPYmplY3Q6IFkuTWFwPHVua25vd24+IHwgWS5BcnJheTx1bmtub3duPiB8IFkuVGV4dCxcbiAgcGF0aDogcmVhZG9ubHkgKHN0cmluZyB8IG51bWJlcilbXVxuKTogdW5rbm93biB7XG4gIGxldCBjdXJyZW50WWpzT2JqZWN0OiB1bmtub3duID0geWpzT2JqZWN0XG5cbiAgbGV0IGkgPSAtMVxuICBmb3IgKGNvbnN0IHBhdGhQYXJ0IG9mIHBhdGgpIHtcbiAgICBpKytcbiAgICAvLyBJZiB3ZSBlbmNvdW50ZXIgYSBZLlRleHQgZHVyaW5nIHBhdGggcmVzb2x1dGlvbiwgcmV0dXJuIGl0IGltbWVkaWF0ZWx5LlxuICAgIC8vIFkuVGV4dCBvYmplY3RzIGRvbid0IHN1cHBvcnQgbmVzdGVkIHBhdGggdHJhdmVyc2FsLCBhbmQgdGhlaXIgdXBkYXRlc1xuICAgIC8vIGFyZSBoYW5kbGVkIHNlcGFyYXRlbHkgYnkgWWpzVGV4dE1vZGVsJ3Mgb3duIHN5bmNocm9uaXphdGlvbiBtZWNoYW5pc20uXG4gICAgaWYgKGN1cnJlbnRZanNPYmplY3QgaW5zdGFuY2VvZiBZLlRleHQpIHtcbiAgICAgIHJldHVybiBjdXJyZW50WWpzT2JqZWN0XG4gICAgfVxuXG4gICAgaWYgKGN1cnJlbnRZanNPYmplY3QgaW5zdGFuY2VvZiBZLk1hcCkge1xuICAgICAgZ2V0T3JDcmVhdGVZanNDb2xsZWN0aW9uQXRvbShjdXJyZW50WWpzT2JqZWN0KS5yZXBvcnRPYnNlcnZlZCgpXG4gICAgICBjb25zdCBrZXkgPSBTdHJpbmcocGF0aFBhcnQpXG4gICAgICBjdXJyZW50WWpzT2JqZWN0ID0gY3VycmVudFlqc09iamVjdC5nZXQoa2V5KVxuICAgIH0gZWxzZSBpZiAoY3VycmVudFlqc09iamVjdCBpbnN0YW5jZW9mIFkuQXJyYXkpIHtcbiAgICAgIGdldE9yQ3JlYXRlWWpzQ29sbGVjdGlvbkF0b20oY3VycmVudFlqc09iamVjdCkucmVwb3J0T2JzZXJ2ZWQoKVxuICAgICAgY29uc3Qga2V5ID0gTnVtYmVyKHBhdGhQYXJ0KVxuICAgICAgY3VycmVudFlqc09iamVjdCA9IGN1cnJlbnRZanNPYmplY3QuZ2V0KGtleSlcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgZmFpbHVyZShcbiAgICAgICAgYFkuTWFwIG9yIFkuQXJyYXkgd2FzIGV4cGVjdGVkIGF0IHBhdGggJHtKU09OLnN0cmluZ2lmeShcbiAgICAgICAgICBwYXRoLnNsaWNlKDAsIGkpXG4gICAgICAgICl9IGluIG9yZGVyIHRvIHJlc29sdmUgcGF0aCAke0pTT04uc3RyaW5naWZ5KHBhdGgpfSwgYnV0IGdvdCAke2N1cnJlbnRZanNPYmplY3R9IGluc3RlYWRgXG4gICAgICApXG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIGN1cnJlbnRZanNPYmplY3Rcbn1cbiIsImltcG9ydCB7IEFueVR5cGUsIGNyZWF0ZUNvbnRleHQgfSBmcm9tIFwibW9ieC1rZXlzdG9uZVwiXG5pbXBvcnQgKiBhcyBZIGZyb20gXCJ5anNcIlxuXG4vKipcbiAqIENvbnRleHQgd2l0aCBpbmZvIG9uIGhvdyBhIG1vYngta2V5c3RvbmUgbW9kZWwgaXMgYm91bmQgdG8gYSBZLmpzIGRhdGEgc3RydWN0dXJlLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFlqc0JpbmRpbmdDb250ZXh0IHtcbiAgLyoqXG4gICAqIFRoZSBZLmpzIGRvY3VtZW50LlxuICAgKi9cbiAgeWpzRG9jOiBZLkRvY1xuXG4gIC8qKlxuICAgKiBUaGUgYm91bmQgWS5qcyBkYXRhIHN0cnVjdHVyZS5cbiAgICovXG4gIHlqc09iamVjdDogWS5NYXA8dW5rbm93bj4gfCBZLkFycmF5PHVua25vd24+IHwgWS5UZXh0XG5cbiAgLyoqXG4gICAqIFRoZSBtb2J4LWtleXN0b25lIG1vZGVsIHR5cGUuXG4gICAqL1xuICBtb2J4S2V5c3RvbmVUeXBlOiBBbnlUeXBlXG5cbiAgLyoqXG4gICAqIFRoZSBvcmlnaW4gc3ltYm9sIHVzZWQgZm9yIHRyYW5zYWN0aW9ucy5cbiAgICovXG4gIHlqc09yaWdpbjogc3ltYm9sXG5cbiAgLyoqXG4gICAqIFRoZSBib3VuZCBtb2J4LWtleXN0b25lIGluc3RhbmNlLlxuICAgKi9cbiAgYm91bmRPYmplY3Q6IHVua25vd25cblxuICAvKipcbiAgICogV2hldGhlciB3ZSBhcmUgY3VycmVudGx5IGFwcGx5aW5nIFkuanMgY2hhbmdlcyB0byB0aGUgbW9ieC1rZXlzdG9uZSBtb2RlbC5cbiAgICovXG4gIGlzQXBwbHlpbmdZanNDaGFuZ2VzVG9Nb2J4S2V5c3RvbmU6IGJvb2xlYW5cbn1cblxuLyoqXG4gKiBDb250ZXh0IHdpdGggaW5mbyBvbiBob3cgYSBtb2J4LWtleXN0b25lIG1vZGVsIGlzIGJvdW5kIHRvIGEgWS5qcyBkYXRhIHN0cnVjdHVyZS5cbiAqL1xuZXhwb3J0IGNvbnN0IHlqc0JpbmRpbmdDb250ZXh0ID0gY3JlYXRlQ29udGV4dDxZanNCaW5kaW5nQ29udGV4dCB8IHVuZGVmaW5lZD4odW5kZWZpbmVkKVxuIiwiaW1wb3J0IHsgY29tcHV0ZWQsIGNyZWF0ZUF0b20sIElBdG9tLCBvYnNlcnZlLCByZWFjdGlvbiB9IGZyb20gXCJtb2J4XCJcbmltcG9ydCB7XG4gIEZyb3plbixcbiAgZnJvemVuLFxuICBnZXRQYXJlbnRUb0NoaWxkUGF0aCxcbiAgTW9kZWwsXG4gIG1vZGVsLFxuICBvblNuYXBzaG90LFxuICB0UHJvcCxcbiAgdHlwZXMsXG59IGZyb20gXCJtb2J4LWtleXN0b25lXCJcbmltcG9ydCAqIGFzIFkgZnJvbSBcInlqc1wiXG5pbXBvcnQgeyBmYWlsdXJlIH0gZnJvbSBcIi4uL3V0aWxzL2Vycm9yXCJcbmltcG9ydCB7IGlzWWpzVmFsdWVEZWxldGVkIH0gZnJvbSBcIi4uL3V0aWxzL2lzWWpzVmFsdWVEZWxldGVkXCJcbmltcG9ydCB7IHJlc29sdmVZanNQYXRoIH0gZnJvbSBcIi4vcmVzb2x2ZVlqc1BhdGhcIlxuaW1wb3J0IHsgWWpzQmluZGluZ0NvbnRleHQsIHlqc0JpbmRpbmdDb250ZXh0IH0gZnJvbSBcIi4veWpzQmluZGluZ0NvbnRleHRcIlxuXG4vLyBEZWx0YVtdW10sIHNpbmNlIGVhY2ggc2luZ2xlIGNoYW5nZSBpcyBhIERlbHRhW11cbi8vIHdlIHVzZSBmcm96ZW4gc28gdGhhdCB3ZSBjYW4gcmV1c2UgZWFjaCBkZWx0YSBjaGFuZ2VcbmNvbnN0IGRlbHRhTGlzdFR5cGUgPSB0eXBlcy5hcnJheSh0eXBlcy5mcm96ZW4odHlwZXMudW5jaGVja2VkPHVua25vd25bXT4oKSkpXG5cbmV4cG9ydCBjb25zdCB5anNUZXh0TW9kZWxJZCA9IFwibW9ieC1rZXlzdG9uZS15anMvWWpzVGV4dE1vZGVsXCJcblxuLyoqXG4gKiBBIG1vYngta2V5c3RvbmUgbW9kZWwgdGhhdCByZXByZXNlbnRzIGEgWWpzLlRleHQgb2JqZWN0LlxuICovXG5AbW9kZWwoeWpzVGV4dE1vZGVsSWQpXG5leHBvcnQgY2xhc3MgWWpzVGV4dE1vZGVsIGV4dGVuZHMgTW9kZWwoe1xuICBkZWx0YUxpc3Q6IHRQcm9wKGRlbHRhTGlzdFR5cGUsICgpID0+IFtdKSxcbn0pIHtcbiAgLyoqXG4gICAqIEhlbHBlciBmdW5jdGlvbiB0byBjcmVhdGUgYSBZanNUZXh0TW9kZWwgaW5zdGFuY2Ugd2l0aCBhIHNpbXBsZSB0ZXh0LlxuICAgKi9cbiAgc3RhdGljIHdpdGhUZXh0KHRleHQ6IHN0cmluZyk6IFlqc1RleHRNb2RlbCB7XG4gICAgcmV0dXJuIG5ldyBEZWNvcmF0ZWRZanNUZXh0TW9kZWwoe1xuICAgICAgZGVsdGFMaXN0OiBbXG4gICAgICAgIGZyb3plbihbXG4gICAgICAgICAge1xuICAgICAgICAgICAgaW5zZXJ0OiB0ZXh0LFxuICAgICAgICAgIH0sXG4gICAgICAgIF0pLFxuICAgICAgXSxcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBZLmpzIHBhdGggZnJvbSB0aGUgYm91bmQgb2JqZWN0IHRvIHRoZSBZanNUZXh0TW9kZWwgaW5zdGFuY2UuXG4gICAqL1xuICBAY29tcHV0ZWRcbiAgcHJpdmF0ZSBnZXQgX3lqc09iamVjdFBhdGgoKSB7XG4gICAgY29uc3QgY3R4ID0geWpzQmluZGluZ0NvbnRleHQuZ2V0KHRoaXMpXG4gICAgaWYgKGN0eD8uYm91bmRPYmplY3QgPT0gbnVsbCkge1xuICAgICAgdGhyb3cgZmFpbHVyZShcbiAgICAgICAgXCJ0aGUgWWpzVGV4dE1vZGVsIGluc3RhbmNlIG11c3QgYmUgcGFydCBvZiBhIGJvdW5kIG9iamVjdCBiZWZvcmUgaXQgY2FuIGJlIGFjY2Vzc2VkXCJcbiAgICAgIClcbiAgICB9XG5cbiAgICBjb25zdCBwYXRoID0gZ2V0UGFyZW50VG9DaGlsZFBhdGgoY3R4LmJvdW5kT2JqZWN0LCB0aGlzKVxuICAgIGlmICghcGF0aCkge1xuICAgICAgdGhyb3cgZmFpbHVyZShcImEgcGF0aCBmcm9tIHRoZSBib3VuZCBvYmplY3QgdG8gdGhlIFlqc1RleHRNb2RlbCBpbnN0YW5jZSBpcyBub3QgYXZhaWxhYmxlXCIpXG4gICAgfVxuXG4gICAgcmV0dXJuIHBhdGhcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgWWpzLlRleHQgb2JqZWN0IHByZXNlbnQgYXQgdGhpcyBtb2J4LWtleXN0b25lIG5vZGUncyBwYXRoLlxuICAgKi9cbiAgQGNvbXB1dGVkXG4gIHByaXZhdGUgZ2V0IF95anNPYmplY3RBdFBhdGgoKTogdW5rbm93biB7XG4gICAgY29uc3QgcGF0aCA9IHRoaXMuX3lqc09iamVjdFBhdGhcblxuICAgIGNvbnN0IGN0eCA9IHlqc0JpbmRpbmdDb250ZXh0LmdldCh0aGlzKSFcblxuICAgIHJldHVybiByZXNvbHZlWWpzUGF0aChjdHgueWpzT2JqZWN0LCBwYXRoKVxuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBZanMuVGV4dCBvYmplY3QgcmVwcmVzZW50ZWQgYnkgdGhpcyBtb2J4LWtleXN0b25lIG5vZGUuXG4gICAqL1xuICBAY29tcHV0ZWRcbiAgZ2V0IHlqc1RleHQoKTogWS5UZXh0IHtcbiAgICBjb25zdCB5anNPYmplY3QgPSB0aGlzLl95anNPYmplY3RBdFBhdGhcblxuICAgIGlmICghKHlqc09iamVjdCBpbnN0YW5jZW9mIFkuVGV4dCkpIHtcbiAgICAgIHRocm93IGZhaWx1cmUoYFkuVGV4dCB3YXMgZXhwZWN0ZWQgYXQgcGF0aCAke0pTT04uc3RyaW5naWZ5KHRoaXMuX3lqc09iamVjdFBhdGgpfWApXG4gICAgfVxuXG4gICAgcmV0dXJuIHlqc09iamVjdFxuICB9XG5cbiAgLyoqXG4gICAqIEF0b20gdGhhdCBnZXRzIGNoYW5nZWQgd2hlbiB0aGUgYXNzb2NpYXRlZCBZLmpzIHRleHQgY2hhbmdlcy5cbiAgICovXG4gIHlqc1RleHRDaGFuZ2VkQXRvbSA9IGNyZWF0ZUF0b20oXCJ5anNUZXh0Q2hhbmdlZEF0b21cIilcblxuICAvKipcbiAgICogVGhlIHRleHQgdmFsdWUgb2YgdGhlIFlqcy5UZXh0IG9iamVjdC5cbiAgICogU2hvcnRjdXQgZm9yIGB5anNUZXh0LnRvU3RyaW5nKClgLCBidXQgY29tcHV0ZWQuXG4gICAqL1xuICBAY29tcHV0ZWRcbiAgZ2V0IHRleHQoKTogc3RyaW5nIHtcbiAgICB0aGlzLnlqc1RleHRDaGFuZ2VkQXRvbS5yZXBvcnRPYnNlcnZlZCgpXG5cbiAgICBjb25zdCBjdHggPSB5anNCaW5kaW5nQ29udGV4dC5nZXQodGhpcylcbiAgICBpZiAoY3R4Py5ib3VuZE9iamVjdCAhPSBudWxsKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCB5anNUZXh0U3RyaW5nID0gdGhpcy55anNUZXh0LnRvU3RyaW5nKClcbiAgICAgICAgLy8gaWYgdGhlIHlqc1RleHQgaXMgZGV0YWNoZWQsIHRvU3RyaW5nKCkgcmV0dXJucyBhbiBlbXB0eSBzdHJpbmdcbiAgICAgICAgLy8gaW4gdGhhdCBjYXNlIHdlIHNob3VsZCB1c2UgdGhlIGRlbHRhTGlzdCBhcyBhIGZhbGxiYWNrXG4gICAgICAgIGlmICh5anNUZXh0U3RyaW5nICE9PSBcIlwiIHx8IHRoaXMuZGVsdGFMaXN0Lmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgIHJldHVybiB5anNUZXh0U3RyaW5nXG4gICAgICAgIH1cbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICAvLyBmYWxsIGJhY2tcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBmYWxsIGJhY2sgdG8gZGVsdGFMaXN0XG4gICAgcmV0dXJuIHRoaXMuZGVsdGFMaXN0VG9UZXh0KClcbiAgfVxuXG4gIHByaXZhdGUgZGVsdGFMaXN0VG9UZXh0KCk6IHN0cmluZyB7XG4gICAgY29uc3QgZG9jID0gbmV3IFkuRG9jKClcbiAgICBjb25zdCB0ZXh0ID0gZG9jLmdldFRleHQoKVxuICAgIHRoaXMuZGVsdGFMaXN0LmZvckVhY2goKGQpID0+IHtcbiAgICAgIHRleHQuYXBwbHlEZWx0YShkLmRhdGEpXG4gICAgfSlcbiAgICByZXR1cm4gdGV4dC50b1N0cmluZygpXG4gIH1cblxuICBwcm90ZWN0ZWQgb25Jbml0KCkge1xuICAgIGNvbnN0IHNob3VsZFJlcGxpY2F0ZVRvWWpzID0gKGN0eDogWWpzQmluZGluZ0NvbnRleHQgfCB1bmRlZmluZWQpOiBjdHggaXMgWWpzQmluZGluZ0NvbnRleHQgPT4ge1xuICAgICAgcmV0dXJuICEhY3R4ICYmICEhY3R4LmJvdW5kT2JqZWN0ICYmICFjdHguaXNBcHBseWluZ1lqc0NoYW5nZXNUb01vYnhLZXlzdG9uZVxuICAgIH1cblxuICAgIGxldCByZWFwcGx5RGVsdGFzVG9ZanNUZXh0ID0gZmFsc2VcbiAgICBjb25zdCBuZXdEZWx0YXM6IEZyb3plbjx1bmtub3duW10+W10gPSBbXVxuXG4gICAgbGV0IGRpc3Bvc2VPYnNlcnZlRGVsdGFMaXN0OiAoKCkgPT4gdm9pZCkgfCB1bmRlZmluZWRcblxuICAgIGNvbnN0IGRpc3Bvc2VSZWFjdGlvblRvRGVsdGFMaXN0UmVmQ2hhbmdlID0gcmVhY3Rpb24oXG4gICAgICAoKSA9PiB0aGlzLiQuZGVsdGFMaXN0LFxuICAgICAgKGRlbHRhTGlzdCkgPT4ge1xuICAgICAgICBkaXNwb3NlT2JzZXJ2ZURlbHRhTGlzdD8uKClcbiAgICAgICAgZGlzcG9zZU9ic2VydmVEZWx0YUxpc3QgPSB1bmRlZmluZWRcblxuICAgICAgICBkaXNwb3NlT2JzZXJ2ZURlbHRhTGlzdCA9IG9ic2VydmUoZGVsdGFMaXN0LCAoY2hhbmdlKSA9PiB7XG4gICAgICAgICAgaWYgKHJlYXBwbHlEZWx0YXNUb1lqc1RleHQpIHtcbiAgICAgICAgICAgIC8vIGFscmVhZHkgZ29ubmEgcmVwbGFjZSB0aGVtIGFsbFxuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICghc2hvdWxkUmVwbGljYXRlVG9ZanMoeWpzQmluZGluZ0NvbnRleHQuZ2V0KHRoaXMpKSkge1xuICAgICAgICAgICAgLy8geWpzIHRleHQgaXMgYWxyZWFkeSB1cCB0byBkYXRlIHdpdGggdGhlc2UgY2hhbmdlc1xuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKFxuICAgICAgICAgICAgY2hhbmdlLnR5cGUgPT09IFwic3BsaWNlXCIgJiZcbiAgICAgICAgICAgIGNoYW5nZS5yZW1vdmVkQ291bnQgPT09IDAgJiZcbiAgICAgICAgICAgIGNoYW5nZS5hZGRlZENvdW50ID4gMCAmJlxuICAgICAgICAgICAgY2hhbmdlLmluZGV4ID09PSB0aGlzLmRlbHRhTGlzdC5sZW5ndGhcbiAgICAgICAgICApIHtcbiAgICAgICAgICAgIC8vIG9wdGltaXphdGlvbiwganVzdCBhZGRpbmcgbmV3IG9uZXMgdG8gdGhlIGVuZFxuICAgICAgICAgICAgbmV3RGVsdGFzLnB1c2goLi4uY2hhbmdlLmFkZGVkKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBhbnkgb3RoZXIgY2hhbmdlLCB3ZSBuZWVkIHRvIHJlYXBwbHkgYWxsIGRlbHRhc1xuICAgICAgICAgICAgcmVhcHBseURlbHRhc1RvWWpzVGV4dCA9IHRydWVcbiAgICAgICAgICB9XG4gICAgICAgIH0pXG4gICAgICB9LFxuICAgICAgeyBmaXJlSW1tZWRpYXRlbHk6IHRydWUgfVxuICAgIClcblxuICAgIGNvbnN0IGRpc3Bvc2VPblNuYXBzaG90ID0gb25TbmFwc2hvdCh0aGlzLCAoKSA9PiB7XG4gICAgICB0cnkge1xuICAgICAgICBpZiAocmVhcHBseURlbHRhc1RvWWpzVGV4dCkge1xuICAgICAgICAgIGNvbnN0IGN0eCA9IHlqc0JpbmRpbmdDb250ZXh0LmdldCh0aGlzKVxuXG4gICAgICAgICAgaWYgKHNob3VsZFJlcGxpY2F0ZVRvWWpzKGN0eCkpIHtcbiAgICAgICAgICAgIGNvbnN0IHsgeWpzVGV4dCB9ID0gdGhpc1xuICAgICAgICAgICAgaWYgKGlzWWpzVmFsdWVEZWxldGVkKHlqc1RleHQpKSB7XG4gICAgICAgICAgICAgIHRocm93IGZhaWx1cmUoXCJjYW5ub3QgcmVhcHBseSBkZWx0YXMgdG8gZGVsZXRlZCBZanMuVGV4dFwiKVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjdHgueWpzRG9jLnRyYW5zYWN0KCgpID0+IHtcbiAgICAgICAgICAgICAgLy8gZGlkbid0IGZpbmQgYSBiZXR0ZXIgd2F5IHRoYW4gdGhpcyB0byByZWFwcGx5IGFsbCBkZWx0YXNcbiAgICAgICAgICAgICAgLy8gd2l0aG91dCBoYXZpbmcgdG8gcmUtY3JlYXRlIHRoZSBZLlRleHQgb2JqZWN0XG4gICAgICAgICAgICAgIGlmICh5anNUZXh0Lmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICB5anNUZXh0LmRlbGV0ZSgwLCB5anNUZXh0Lmxlbmd0aClcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIHRoaXMuZGVsdGFMaXN0LmZvckVhY2goKGZyb3plbkRlbHRhcykgPT4ge1xuICAgICAgICAgICAgICAgIHlqc1RleHQuYXBwbHlEZWx0YShmcm96ZW5EZWx0YXMuZGF0YSlcbiAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH0sIGN0eC55anNPcmlnaW4pXG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKG5ld0RlbHRhcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgY29uc3QgY3R4ID0geWpzQmluZGluZ0NvbnRleHQuZ2V0KHRoaXMpXG5cbiAgICAgICAgICBpZiAoc2hvdWxkUmVwbGljYXRlVG9ZanMoY3R4KSkge1xuICAgICAgICAgICAgY29uc3QgeyB5anNUZXh0IH0gPSB0aGlzXG4gICAgICAgICAgICBpZiAoaXNZanNWYWx1ZURlbGV0ZWQoeWpzVGV4dCkpIHtcbiAgICAgICAgICAgICAgdGhyb3cgZmFpbHVyZShcImNhbm5vdCByZWFwcGx5IGRlbHRhcyB0byBkZWxldGVkIFlqcy5UZXh0XCIpXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGN0eC55anNEb2MudHJhbnNhY3QoKCkgPT4ge1xuICAgICAgICAgICAgICBuZXdEZWx0YXMuZm9yRWFjaCgoZnJvemVuRGVsdGFzKSA9PiB7XG4gICAgICAgICAgICAgICAgeWpzVGV4dC5hcHBseURlbHRhKGZyb3plbkRlbHRhcy5kYXRhKVxuICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfSwgY3R4Lnlqc09yaWdpbilcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0gZmluYWxseSB7XG4gICAgICAgIHJlYXBwbHlEZWx0YXNUb1lqc1RleHQgPSBmYWxzZVxuICAgICAgICBuZXdEZWx0YXMubGVuZ3RoID0gMFxuICAgICAgfVxuICAgIH0pXG5cbiAgICBjb25zdCBkaXBvc2VZanNUZXh0Q2hhbmdlZEF0b20gPSBob29rWWpzVGV4dENoYW5nZWRBdG9tKFxuICAgICAgKCkgPT4gdGhpcy55anNUZXh0LFxuICAgICAgdGhpcy55anNUZXh0Q2hhbmdlZEF0b21cbiAgICApXG5cbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgZGlzcG9zZU9uU25hcHNob3QoKVxuICAgICAgZGlzcG9zZVJlYWN0aW9uVG9EZWx0YUxpc3RSZWZDaGFuZ2UoKVxuICAgICAgZGlzcG9zZU9ic2VydmVEZWx0YUxpc3Q/LigpXG4gICAgICBkaXNwb3NlT2JzZXJ2ZURlbHRhTGlzdCA9IHVuZGVmaW5lZFxuXG4gICAgICBkaXBvc2VZanNUZXh0Q2hhbmdlZEF0b20oKVxuICAgIH1cbiAgfVxufVxuXG4vLyB3ZSB1c2UgdGhpcyB0cmljayBqdXN0IHRvIGF2b2lkIGEgYmFiZWwgYnVnIHRoYXQgY2F1c2VzIGNsYXNzZXMgdXNlZCBpbnNpZGUgY2xhc3NlcyBub3QgdG8gYmUgb3ZlcnJpZGVuXG4vLyBieSB0aGUgZGVjb3JhdG9yXG5jb25zdCBEZWNvcmF0ZWRZanNUZXh0TW9kZWwgPSBZanNUZXh0TW9kZWxcblxuZnVuY3Rpb24gaG9va1lqc1RleHRDaGFuZ2VkQXRvbShnZXRZanNUZXh0OiAoKSA9PiBZLlRleHQsIHRleHRDaGFuZ2VkQXRvbTogSUF0b20pIHtcbiAgbGV0IGRpc3Bvc2VPYnNlcnZlWWpzVGV4dDogKCgpID0+IHZvaWQpIHwgdW5kZWZpbmVkXG5cbiAgY29uc3Qgb2JzZXJ2ZUZuID0gKCkgPT4ge1xuICAgIHRleHRDaGFuZ2VkQXRvbS5yZXBvcnRDaGFuZ2VkKClcbiAgfVxuXG4gIGNvbnN0IGRpc3Bvc2VSZWFjdGlvblRvWVRleHRDaGFuZ2UgPSByZWFjdGlvbihcbiAgICAoKSA9PiB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCB5anNUZXh0ID0gZ2V0WWpzVGV4dCgpXG4gICAgICAgIHJldHVybiBpc1lqc1ZhbHVlRGVsZXRlZCh5anNUZXh0KSA/IHVuZGVmaW5lZCA6IHlqc1RleHRcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICByZXR1cm4gdW5kZWZpbmVkXG4gICAgICB9XG4gICAgfSxcbiAgICAoeWpzVGV4dCkgPT4ge1xuICAgICAgZGlzcG9zZU9ic2VydmVZanNUZXh0Py4oKVxuICAgICAgZGlzcG9zZU9ic2VydmVZanNUZXh0ID0gdW5kZWZpbmVkXG5cbiAgICAgIGlmICh5anNUZXh0KSB7XG4gICAgICAgIHlqc1RleHQub2JzZXJ2ZShvYnNlcnZlRm4pXG5cbiAgICAgICAgZGlzcG9zZU9ic2VydmVZanNUZXh0ID0gKCkgPT4ge1xuICAgICAgICAgIHlqc1RleHQudW5vYnNlcnZlKG9ic2VydmVGbilcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICB0ZXh0Q2hhbmdlZEF0b20ucmVwb3J0Q2hhbmdlZCgpXG4gICAgfSxcbiAgICB7XG4gICAgICBmaXJlSW1tZWRpYXRlbHk6IHRydWUsXG4gICAgfVxuICApXG5cbiAgcmV0dXJuICgpID0+IHtcbiAgICBkaXNwb3NlUmVhY3Rpb25Ub1lUZXh0Q2hhbmdlKClcbiAgICBkaXNwb3NlT2JzZXJ2ZVlqc1RleHQ/LigpXG4gICAgZGlzcG9zZU9ic2VydmVZanNUZXh0ID0gdW5kZWZpbmVkXG4gIH1cbn1cbiIsImltcG9ydCAqIGFzIFkgZnJvbSBcInlqc1wiXG5cbi8qKlxuICogV2Vha01hcCB0aGF0IHRyYWNrcyB3aGljaCBzbmFwc2hvdCBlYWNoIFkuanMgY29udGFpbmVyIHdhcyBsYXN0IHN5bmNlZCBmcm9tLlxuICogVGhpcyBpcyB1c2VkIGR1cmluZyByZWNvbmNpbGlhdGlvbiB0byBza2lwIGNvbnRhaW5lcnMgdGhhdCBhcmUgYWxyZWFkeSB1cC10by1kYXRlLlxuICpcbiAqIFRoZSBrZXkgaXMgdGhlIFkuanMgY29udGFpbmVyIChZLk1hcCBvciBZLkFycmF5KS5cbiAqIFRoZSB2YWx1ZSBpcyB0aGUgc25hcHNob3QgKHBsYWluIG9iamVjdCBvciBhcnJheSkgdGhhdCB3YXMgbGFzdCBzeW5jZWQgdG8gaXQuXG4gKi9cbmV4cG9ydCBjb25zdCB5anNDb250YWluZXJUb1NuYXBzaG90ID0gbmV3IFdlYWtNYXA8WS5NYXA8YW55PiB8IFkuQXJyYXk8YW55PiwgdW5rbm93bj4oKVxuXG4vKipcbiAqIFVwZGF0ZXMgdGhlIHNuYXBzaG90IHRyYWNraW5nIGZvciBhIFkuanMgY29udGFpbmVyLlxuICogQ2FsbCB0aGlzIGFmdGVyIHN5bmNpbmcgYSBzbmFwc2hvdCB0byBhIFkuanMgY29udGFpbmVyLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2V0WWpzQ29udGFpbmVyU25hcHNob3QoXG4gIGNvbnRhaW5lcjogWS5NYXA8YW55PiB8IFkuQXJyYXk8YW55PixcbiAgc25hcHNob3Q6IHVua25vd25cbik6IHZvaWQge1xuICB5anNDb250YWluZXJUb1NuYXBzaG90LnNldChjb250YWluZXIsIHNuYXBzaG90KVxufVxuXG4vKipcbiAqIEdldHMgdGhlIGxhc3Qgc3luY2VkIHNuYXBzaG90IGZvciBhIFkuanMgY29udGFpbmVyLlxuICogUmV0dXJucyB1bmRlZmluZWQgaWYgdGhlIGNvbnRhaW5lciBoYXMgbmV2ZXIgYmVlbiBzeW5jZWQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRZanNDb250YWluZXJTbmFwc2hvdChjb250YWluZXI6IFkuTWFwPGFueT4gfCBZLkFycmF5PGFueT4pOiB1bmtub3duIHtcbiAgcmV0dXJuIHlqc0NvbnRhaW5lclRvU25hcHNob3QuZ2V0KGNvbnRhaW5lcilcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYSBZLmpzIGNvbnRhaW5lciBpcyB1cC10by1kYXRlIHdpdGggdGhlIGdpdmVuIHNuYXBzaG90LlxuICogVXNlcyByZWZlcmVuY2UgZXF1YWxpdHkgdG8gY2hlY2sgaWYgdGhlIHNuYXBzaG90IGlzIHRoZSBzYW1lLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNZanNDb250YWluZXJVcFRvRGF0ZShcbiAgY29udGFpbmVyOiBZLk1hcDxhbnk+IHwgWS5BcnJheTxhbnk+LFxuICBzbmFwc2hvdDogdW5rbm93blxuKTogYm9vbGVhbiB7XG4gIHJldHVybiB5anNDb250YWluZXJUb1NuYXBzaG90LmdldChjb250YWluZXIpID09PSBzbmFwc2hvdFxufVxuIiwiaW1wb3J0IHsgZnJvemVuS2V5LCBtb2RlbFR5cGVLZXksIFNuYXBzaG90T3V0T2YgfSBmcm9tIFwibW9ieC1rZXlzdG9uZVwiXG5pbXBvcnQgKiBhcyBZIGZyb20gXCJ5anNcIlxuaW1wb3J0IHsgUGxhaW5BcnJheSwgUGxhaW5PYmplY3QsIFBsYWluUHJpbWl0aXZlLCBQbGFpblZhbHVlIH0gZnJvbSBcIi4uL3BsYWluVHlwZXNcIlxuaW1wb3J0IHsgWWpzRGF0YSB9IGZyb20gXCIuL2NvbnZlcnRZanNEYXRhVG9Kc29uXCJcbmltcG9ydCB7IFlqc1RleHRNb2RlbCwgeWpzVGV4dE1vZGVsSWQgfSBmcm9tIFwiLi9ZanNUZXh0TW9kZWxcIlxuaW1wb3J0IHsgaXNZanNDb250YWluZXJVcFRvRGF0ZSwgc2V0WWpzQ29udGFpbmVyU25hcHNob3QgfSBmcm9tIFwiLi95anNTbmFwc2hvdFRyYWNraW5nXCJcblxuLyoqXG4gKiBPcHRpb25zIGZvciBhcHBseWluZyBKU09OIGRhdGEgdG8gWS5qcyBkYXRhIHN0cnVjdHVyZXMuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQXBwbHlKc29uVG9ZanNPcHRpb25zIHtcbiAgLyoqXG4gICAqIFRoZSBtb2RlIHRvIHVzZSB3aGVuIGFwcGx5aW5nIEpTT04gZGF0YSB0byBZLmpzIGRhdGEgc3RydWN0dXJlcy5cbiAgICogLSBgYWRkYDogQ3JlYXRlcyBuZXcgWS5qcyBjb250YWluZXJzIGZvciBvYmplY3RzL2FycmF5cyAoZGVmYXVsdCwgYmFja3dhcmRzIGNvbXBhdGlibGUpXG4gICAqIC0gYG1lcmdlYDogUmVjdXJzaXZlbHkgbWVyZ2VzIHZhbHVlcywgcHJlc2VydmluZyBleGlzdGluZyBjb250YWluZXIgcmVmZXJlbmNlcyB3aGVyZSBwb3NzaWJsZVxuICAgKi9cbiAgbW9kZT86IFwiYWRkXCIgfCBcIm1lcmdlXCJcbn1cblxuZnVuY3Rpb24gaXNQbGFpblByaW1pdGl2ZSh2OiBQbGFpblZhbHVlKTogdiBpcyBQbGFpblByaW1pdGl2ZSB7XG4gIGNvbnN0IHQgPSB0eXBlb2YgdlxuICByZXR1cm4gdCA9PT0gXCJzdHJpbmdcIiB8fCB0ID09PSBcIm51bWJlclwiIHx8IHQgPT09IFwiYm9vbGVhblwiIHx8IHYgPT09IG51bGwgfHwgdiA9PT0gdW5kZWZpbmVkXG59XG5cbmZ1bmN0aW9uIGlzUGxhaW5BcnJheSh2OiBQbGFpblZhbHVlKTogdiBpcyBQbGFpbkFycmF5IHtcbiAgcmV0dXJuIEFycmF5LmlzQXJyYXkodilcbn1cblxuZnVuY3Rpb24gaXNQbGFpbk9iamVjdCh2OiBQbGFpblZhbHVlKTogdiBpcyBQbGFpbk9iamVjdCB7XG4gIHJldHVybiB0eXBlb2YgdiA9PT0gXCJvYmplY3RcIiAmJiB2ICE9PSBudWxsICYmICFBcnJheS5pc0FycmF5KHYpXG59XG5cbi8qKlxuICogQ29udmVydHMgYSBwbGFpbiB2YWx1ZSB0byBhIFkuanMgZGF0YSBzdHJ1Y3R1cmUuXG4gKiBPYmplY3RzIGFyZSBjb252ZXJ0ZWQgdG8gWS5NYXBzLCBhcnJheXMgdG8gWS5BcnJheXMsIHByaW1pdGl2ZXMgYXJlIHVudG91Y2hlZC5cbiAqIEZyb3plbiB2YWx1ZXMgYXJlIGEgc3BlY2lhbCBjYXNlIGFuZCB0aGV5IGFyZSBrZXB0IGFzIGltbXV0YWJsZSBwbGFpbiB2YWx1ZXMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb252ZXJ0SnNvblRvWWpzRGF0YSh2OiBQbGFpblZhbHVlKTogWWpzRGF0YSB7XG4gIGlmIChpc1BsYWluUHJpbWl0aXZlKHYpKSB7XG4gICAgcmV0dXJuIHZcbiAgfVxuXG4gIGlmIChpc1BsYWluQXJyYXkodikpIHtcbiAgICBjb25zdCBhcnIgPSBuZXcgWS5BcnJheSgpXG4gICAgYXBwbHlKc29uQXJyYXlUb1lBcnJheShhcnIsIHYpXG4gICAgcmV0dXJuIGFyclxuICB9XG5cbiAgaWYgKGlzUGxhaW5PYmplY3QodikpIHtcbiAgICBpZiAodltmcm96ZW5LZXldID09PSB0cnVlKSB7XG4gICAgICAvLyBmcm96ZW4gdmFsdWUsIHNhdmUgYXMgaW1tdXRhYmxlIG9iamVjdFxuICAgICAgcmV0dXJuIHZcbiAgICB9XG5cbiAgICBpZiAodlttb2RlbFR5cGVLZXldID09PSB5anNUZXh0TW9kZWxJZCkge1xuICAgICAgY29uc3QgdGV4dCA9IG5ldyBZLlRleHQoKVxuICAgICAgY29uc3QgeWpzVGV4dE1vZGVsID0gdiBhcyB1bmtub3duIGFzIFNuYXBzaG90T3V0T2Y8WWpzVGV4dE1vZGVsPlxuICAgICAgeWpzVGV4dE1vZGVsLmRlbHRhTGlzdC5mb3JFYWNoKChmcm96ZW5EZWx0YXMpID0+IHtcbiAgICAgICAgdGV4dC5hcHBseURlbHRhKGZyb3plbkRlbHRhcy5kYXRhKVxuICAgICAgfSlcbiAgICAgIHJldHVybiB0ZXh0XG4gICAgfVxuXG4gICAgY29uc3QgbWFwID0gbmV3IFkuTWFwKClcbiAgICBhcHBseUpzb25PYmplY3RUb1lNYXAobWFwLCB2KVxuICAgIHJldHVybiBtYXBcbiAgfVxuXG4gIHRocm93IG5ldyBFcnJvcihgdW5zdXBwb3J0ZWQgdmFsdWUgdHlwZTogJHt2fWApXG59XG5cbi8qKlxuICogQXBwbGllcyBhIEpTT04gYXJyYXkgdG8gYSBZLkFycmF5LCB1c2luZyB0aGUgY29udmVydEpzb25Ub1lqc0RhdGEgdG8gY29udmVydCB0aGUgdmFsdWVzLlxuICpcbiAqIEBwYXJhbSBkZXN0IFRoZSBkZXN0aW5hdGlvbiBZLkFycmF5LlxuICogQHBhcmFtIHNvdXJjZSBUaGUgc291cmNlIEpTT04gYXJyYXkuXG4gKiBAcGFyYW0gb3B0aW9ucyBPcHRpb25zIGZvciBhcHBseWluZyB0aGUgSlNPTiBkYXRhLlxuICovXG5leHBvcnQgY29uc3QgYXBwbHlKc29uQXJyYXlUb1lBcnJheSA