You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-desktop/test/session/unit/sending/PendingMessageCache_test.js

219 lines
31 KiB
JavaScript

var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
var import_chai = require("chai");
var sinon = __toESM(require("sinon"));
var _ = __toESM(require("lodash"));
var import_utils = require("../../../../session/utils");
var import_test_utils = require("../../../../test/test-utils");
var import_PendingMessageCache = require("../../../../session/sending/PendingMessageCache");
describe("PendingMessageCache", () => {
const sandbox = sinon.createSandbox();
let data;
let pendingMessageCacheStub;
beforeEach(() => {
const storageID = "pendingMessages";
data = {
id: storageID,
value: "[]"
};
import_test_utils.TestUtils.stubData("getItemById").withArgs("pendingMessages").callsFake(async () => {
return data;
});
import_test_utils.TestUtils.stubData("createOrUpdateItem").callsFake((item) => {
if (item.id === storageID) {
data = item;
}
});
pendingMessageCacheStub = new import_PendingMessageCache.PendingMessageCache();
});
afterEach(() => {
sandbox.restore();
import_test_utils.TestUtils.restoreStubs();
});
it("can initialize cache", async () => {
const cache = await pendingMessageCacheStub.getAllPending();
(0, import_chai.expect)(cache).to.be.instanceOf(Array);
(0, import_chai.expect)(cache).to.have.length(0);
});
it("can add to cache", async () => {
const device = import_test_utils.TestUtils.generateFakePubKey();
const message = import_test_utils.TestUtils.generateVisibleMessage();
const rawMessage = await import_utils.MessageUtils.toRawMessage(device, message);
await pendingMessageCacheStub.add(device, message);
const finalCache = await pendingMessageCacheStub.getAllPending();
(0, import_chai.expect)(finalCache).to.have.length(1);
const addedMessage = finalCache[0];
(0, import_chai.expect)(addedMessage.device).to.deep.equal(rawMessage.device);
});
it("can add multiple messages belonging to the same user", async () => {
const device = import_test_utils.TestUtils.generateFakePubKey();
await pendingMessageCacheStub.add(device, import_test_utils.TestUtils.generateVisibleMessage());
await import_test_utils.TestUtils.timeout(5);
await pendingMessageCacheStub.add(device, import_test_utils.TestUtils.generateVisibleMessage());
await import_test_utils.TestUtils.timeout(5);
await pendingMessageCacheStub.add(device, import_test_utils.TestUtils.generateVisibleMessage());
const finalCache = await pendingMessageCacheStub.getAllPending();
(0, import_chai.expect)(finalCache).to.have.length(3);
});
it("can remove from cache", async () => {
const device = import_test_utils.TestUtils.generateFakePubKey();
const message = import_test_utils.TestUtils.generateVisibleMessage();
const rawMessage = await import_utils.MessageUtils.toRawMessage(device, message);
await pendingMessageCacheStub.add(device, message);
const initialCache = await pendingMessageCacheStub.getAllPending();
(0, import_chai.expect)(initialCache).to.have.length(1);
await pendingMessageCacheStub.remove(rawMessage);
const finalCache = await pendingMessageCacheStub.getAllPending();
(0, import_chai.expect)(finalCache).to.have.length(0);
});
it("should only remove messages with different identifier and device", async () => {
const device = import_test_utils.TestUtils.generateFakePubKey();
const message = import_test_utils.TestUtils.generateVisibleMessage();
const rawMessage = await import_utils.MessageUtils.toRawMessage(device, message);
await pendingMessageCacheStub.add(device, message);
await import_test_utils.TestUtils.timeout(5);
const one = await pendingMessageCacheStub.add(device, import_test_utils.TestUtils.generateVisibleMessage());
const two = await pendingMessageCacheStub.add(import_test_utils.TestUtils.generateFakePubKey(), message);
const initialCache = await pendingMessageCacheStub.getAllPending();
(0, import_chai.expect)(initialCache).to.have.length(3);
await pendingMessageCacheStub.remove(rawMessage);
const finalCache = await pendingMessageCacheStub.getAllPending();
(0, import_chai.expect)(finalCache).to.have.length(2);
(0, import_chai.expect)(finalCache).to.have.deep.members([one, two]);
});
it("can get devices", async () => {
const cacheItems = [
{
device: import_test_utils.TestUtils.generateFakePubKey(),
message: import_test_utils.TestUtils.generateVisibleMessage()
},
{
device: import_test_utils.TestUtils.generateFakePubKey(),
message: import_test_utils.TestUtils.generateVisibleMessage()
},
{
device: import_test_utils.TestUtils.generateFakePubKey(),
message: import_test_utils.TestUtils.generateVisibleMessage()
}
];
for (const item of cacheItems) {
await pendingMessageCacheStub.add(item.device, item.message);
}
const cache = await pendingMessageCacheStub.getAllPending();
(0, import_chai.expect)(cache).to.have.length(cacheItems.length);
const devicesKeys = cacheItems.map((item) => item.device.key);
const pulledDevices = await pendingMessageCacheStub.getDevices();
const pulledDevicesKeys = pulledDevices.map((d) => d.key);
(0, import_chai.expect)(pulledDevicesKeys).to.have.members(devicesKeys);
});
it("can get pending for device", async () => {
const cacheItems = [
{
device: import_test_utils.TestUtils.generateFakePubKey(),
message: import_test_utils.TestUtils.generateVisibleMessage()
},
{
device: import_test_utils.TestUtils.generateFakePubKey(),
message: import_test_utils.TestUtils.generateVisibleMessage()
}
];
for (const item of cacheItems) {
await pendingMessageCacheStub.add(item.device, item.message);
}
const initialCache = await pendingMessageCacheStub.getAllPending();
(0, import_chai.expect)(initialCache).to.have.length(cacheItems.length);
for (const item of cacheItems) {
const pendingForDevice = await pendingMessageCacheStub.getForDevice(item.device);
(0, import_chai.expect)(pendingForDevice).to.have.length(1);
(0, import_chai.expect)(pendingForDevice[0].device).to.equal(item.device.key);
}
});
it("can find nothing when empty", async () => {
const device = import_test_utils.TestUtils.generateFakePubKey();
const message = import_test_utils.TestUtils.generateVisibleMessage();
const rawMessage = await import_utils.MessageUtils.toRawMessage(device, message);
const foundMessage = pendingMessageCacheStub.find(rawMessage);
(0, import_chai.expect)(foundMessage, "a message was found in empty cache").to.be.undefined;
});
it("can find message in cache", async () => {
const device = import_test_utils.TestUtils.generateFakePubKey();
const message = import_test_utils.TestUtils.generateVisibleMessage();
const rawMessage = await import_utils.MessageUtils.toRawMessage(device, message);
await pendingMessageCacheStub.add(device, message);
const finalCache = await pendingMessageCacheStub.getAllPending();
(0, import_chai.expect)(finalCache).to.have.length(1);
const foundMessage = pendingMessageCacheStub.find(rawMessage);
(0, import_chai.expect)(foundMessage, "message not found in cache").to.be.ok;
foundMessage && (0, import_chai.expect)(foundMessage.device).to.equal(device.key);
});
it("can clear cache", async () => {
const cacheItems = [
{
device: import_test_utils.TestUtils.generateFakePubKey(),
message: import_test_utils.TestUtils.generateVisibleMessage()
},
{
device: import_test_utils.TestUtils.generateFakePubKey(),
message: import_test_utils.TestUtils.generateVisibleMessage()
},
{
device: import_test_utils.TestUtils.generateFakePubKey(),
message: import_test_utils.TestUtils.generateVisibleMessage()
}
];
for (const item of cacheItems) {
await pendingMessageCacheStub.add(item.device, item.message);
}
const initialCache = await pendingMessageCacheStub.getAllPending();
(0, import_chai.expect)(initialCache).to.have.length(cacheItems.length);
await pendingMessageCacheStub.clear();
const finalCache = await pendingMessageCacheStub.getAllPending();
(0, import_chai.expect)(finalCache).to.have.length(0);
});
it("can restore from db", async () => {
const cacheItems = [
{
device: import_test_utils.TestUtils.generateFakePubKey(),
message: import_test_utils.TestUtils.generateVisibleMessage()
},
{
device: import_test_utils.TestUtils.generateFakePubKey(),
message: import_test_utils.TestUtils.generateVisibleMessage()
},
{
device: import_test_utils.TestUtils.generateFakePubKey(),
message: import_test_utils.TestUtils.generateVisibleMessage()
}
];
for (const item of cacheItems) {
await pendingMessageCacheStub.add(item.device, item.message);
}
const addedMessages = await pendingMessageCacheStub.getAllPending();
(0, import_chai.expect)(addedMessages).to.have.length(cacheItems.length);
const freshCache = new import_PendingMessageCache.PendingMessageCache();
const rebuiltMessages = await freshCache.getAllPending();
for (const [index, message] of rebuiltMessages.entries()) {
const addedMessage = addedMessages[index];
const buffersCompare = Buffer.compare(message.plainTextBuffer, addedMessage.plainTextBuffer) === 0;
(0, import_chai.expect)(buffersCompare).to.equal(true, "buffers were not loaded properly from database");
const trimmedAdded = _.omit(addedMessage, ["plainTextBuffer"]);
const trimmedRebuilt = _.omit(message, ["plainTextBuffer"]);
(0, import_chai.expect)(_.isEqual(trimmedAdded, trimmedRebuilt)).to.equal(true, "cached messages were not rebuilt properly");
}
});
});
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vLi4vdHMvdGVzdC9zZXNzaW9uL3VuaXQvc2VuZGluZy9QZW5kaW5nTWVzc2FnZUNhY2hlX3Rlc3QudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi8vIHRzbGludDpkaXNhYmxlOiBuby1pbXBsaWNpdC1kZXBlbmRlbmNpZXMgbWF4LWZ1bmMtYm9keS1sZW5ndGggbm8tdW51c2VkLWV4cHJlc3Npb25cbmltcG9ydCB7IGV4cGVjdCB9IGZyb20gJ2NoYWknO1xuaW1wb3J0ICogYXMgc2lub24gZnJvbSAnc2lub24nO1xuaW1wb3J0ICogYXMgXyBmcm9tICdsb2Rhc2gnO1xuaW1wb3J0IHsgTWVzc2FnZVV0aWxzIH0gZnJvbSAnLi4vLi4vLi4vLi4vc2Vzc2lvbi91dGlscyc7XG5pbXBvcnQgeyBUZXN0VXRpbHMgfSBmcm9tICcuLi8uLi8uLi8uLi90ZXN0L3Rlc3QtdXRpbHMnO1xuaW1wb3J0IHsgUGVuZGluZ01lc3NhZ2VDYWNoZSB9IGZyb20gJy4uLy4uLy4uLy4uL3Nlc3Npb24vc2VuZGluZy9QZW5kaW5nTWVzc2FnZUNhY2hlJztcblxuLy8gRXF1aXZhbGVudCB0byBEYXRhLlN0b3JhZ2VJdGVtXG5pbnRlcmZhY2UgU3RvcmFnZUl0ZW0ge1xuICBpZDogc3RyaW5nO1xuICB2YWx1ZTogYW55O1xufVxuXG5kZXNjcmliZSgnUGVuZGluZ01lc3NhZ2VDYWNoZScsICgpID0+IHtcbiAgY29uc3Qgc2FuZGJveCA9IHNpbm9uLmNyZWF0ZVNhbmRib3goKTtcbiAgLy8gSW5pdGlhbGl6ZSBuZXcgc3R1YmJlZCBjYWNoZVxuICBsZXQgZGF0YTogU3RvcmFnZUl0ZW07XG4gIGxldCBwZW5kaW5nTWVzc2FnZUNhY2hlU3R1YjogUGVuZGluZ01lc3NhZ2VDYWNoZTtcblxuICBiZWZvcmVFYWNoKCgpID0+IHtcbiAgICAvLyBTdHViIG91dCBtZXRob2RzIHdoaWNoIHRvdWNoIHRoZSBkYXRhYmFzZVxuICAgIGNvbnN0IHN0b3JhZ2VJRCA9ICdwZW5kaW5nTWVzc2FnZXMnO1xuICAgIGRhdGEgPSB7XG4gICAgICBpZDogc3RvcmFnZUlELFxuICAgICAgdmFsdWU6ICdbXScsXG4gICAgfTtcblxuICAgIFRlc3RVdGlscy5zdHViRGF0YSgnZ2V0SXRlbUJ5SWQnKVxuICAgICAgLndpdGhBcmdzKCdwZW5kaW5nTWVzc2FnZXMnKVxuICAgICAgLmNhbGxzRmFrZShhc3luYyAoKSA9PiB7XG4gICAgICAgIHJldHVybiBkYXRhO1xuICAgICAgfSk7XG5cbiAgICBUZXN0VXRpbHMuc3R1YkRhdGEoJ2NyZWF0ZU9yVXBkYXRlSXRlbScpLmNhbGxzRmFrZSgoaXRlbTogU3RvcmFnZUl0ZW0pID0+IHtcbiAgICAgIGlmIChpdGVtLmlkID09PSBzdG9yYWdlSUQpIHtcbiAgICAgICAgZGF0YSA9IGl0ZW07XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICBwZW5kaW5nTWVzc2FnZUNhY2hlU3R1YiA9IG5ldyBQZW5kaW5nTWVzc2FnZUNhY2hlKCk7XG4gIH0pO1xuXG4gIGFmdGVyRWFjaCgoKSA9PiB7XG4gICAgc2FuZGJveC5yZXN0b3JlKCk7XG4gICAgVGVzdFV0aWxzLnJlc3RvcmVTdHVicygpO1xuICB9KTtcblxuICBpdCgnY2FuIGluaXRpYWxpemUgY2FjaGUnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgY2FjaGUgPSBhd2FpdCBwZW5kaW5nTWVzc2FnZUNhY2hlU3R1Yi5nZXRBbGxQZW5kaW5nKCk7XG5cbiAgICAvLyBXZSBleHBlY3QgdGhlIGNhY2hlIHRvIGluaXRpYWxpc2UgYXMgYW4gZW1wdHkgYXJyYXlcbiAgICBleHBlY3QoY2FjaGUpLnRvLmJlLmluc3RhbmNlT2YoQXJyYXkpO1xuICAgIGV4cGVjdChjYWNoZSkudG8uaGF2ZS5sZW5ndGgoMCk7XG4gIH0pO1xuXG4gIGl0KCdjYW4gYWRkIHRvIGNhY2hlJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGRldmljZSA9IFRlc3RVdGlscy5nZW5lcmF0ZUZha2VQdWJLZXkoKTtcbiAgICBjb25zdCBtZXNzYWdlID0gVGVzdFV0aWxzLmdlbmVyYXRlVmlzaWJsZU1lc3NhZ2UoKTtcbiAgICBjb25zdCByYXdNZXNzYWdlID0gYXdhaXQgTWVzc2FnZVV0aWxzLnRvUmF3TWVzc2FnZShkZXZpY2UsIG1lc3NhZ2UpO1xuXG4gICAgYXdhaXQgcGVuZGluZ01lc3NhZ2VDYWNoZVN0dWIuYWRkKGRldmljZSwgbWVzc2FnZSk7XG5cbiAgICAvLyBWZXJpZnkgdGhhdCB0aGUgbWVzc2FnZSBpcyBpbiB0aGUgY2FjaGVcbiAgICBjb25zdCBmaW5hbENhY2hlID0gYXdhaXQgcGVuZGluZ01lc3NhZ2VDYWNoZVN0dWIuZ2V0QWxsUGVuZGluZygpO1xuXG4gICAgZXhwZWN0KGZpbmFsQ2FjaGUpLnRvLmhhdmUubGVuZ3RoKDEpO1xuXG4gICAgY29uc3QgYWRkZWRNZXNzYWdlID0gZmluYWxDYWNoZVswXTtcbiAgICBleHBlY3QoYWRkZWRNZXNzYWdlLmRldmljZSkudG8uZGVlcC5lcXVhbChyYXdNZXNzYWdlLmRldmljZSk7XG4gIH0pO1xuXG4gIGl0KCdjYW4gYWRkIG11bHRpcGxlIG1lc3NhZ2VzIGJlbG9uZ2luZyB0byB0aGUgc2FtZSB1c2VyJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGRldmljZSA9IFRlc3RVdGlscy5nZW5lcmF0ZUZha2VQdWJLZXkoKTtcblxuICAgIGF3YWl0IHBlbmRpbmdNZXNzYWdlQ2FjaGVTdHViLmFkZChkZXZpY2UsIFRlc3RVdGlscy5nZW5lcmF0ZVZpc2libGVNZXNzYWdlKCkpO1xuICAgIC8vIFdlIGhhdmUgdG8gdGltZW91dCBoZXJlIG90aGVyd2lzZSBpdCdzIHByb2Nlc3NlZCB0b28gZmFzdCBhbmQgbWVzc2FnZXMgc3RhcnQgaGF2aW5nIHRoZSBzYW1lIHRpbWVzdGFtcFxuICAgIGF3YWl0IFRlc3RVdGlscy50aW1lb3V0KDUpO1xuICAgIGF3YWl0IHBlbmRpbmdNZXNzYWdlQ2FjaGVTdHViLmFkZChkZXZpY2UsIFRlc3RVdGlscy5nZW5lcmF0ZVZpc2libGVNZXNzYWdlKCkpO1xuICAgIGF3YWl0IFRlc3RVdGlscy50aW1lb3V0KDUpO1xuICAgIGF3YWl0IHBlbmRpbmdNZXNzYWdlQ2FjaGVTdHViLmFkZChkZXZpY2UsIFRlc3RVdGlscy5nZW5lcmF0ZVZpc2libGVNZXNzYWdlKCkpO1xuXG4gICAgLy8gVmVyaWZ5IHRoYXQgdGhlIG1lc3NhZ2UgaXMgaW4gdGhlIGNhY2hlXG4gICAgY29uc3QgZmluYWxDYWNoZSA9IGF3YWl0IHBlbmRpbmdNZXNzYWdlQ2FjaGVTdHViLmdldEFsbFBlbmRpbmcoKTtcblxuICAgIGV4cGVjdChmaW5hbENhY2hlKS50by5oYXZlLmxlbmd0aCgzKTtcbiAgfSk7XG5cbiAgaXQoJ2NhbiByZW1vdmUgZnJvbSBjYWNoZScsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBkZXZpY2UgPSBUZXN0VXRpbHMuZ2VuZXJhdGVGYWtlUHViS2V5KCk7XG4gICAgY29uc3QgbWVzc2FnZSA9IFRlc3RVdGlscy5nZW5lcmF0ZVZpc2libGVNZXNzYWdlKCk7XG4gICAgY29uc3QgcmF3TWVzc2FnZSA9IGF3YWl0IE1lc3NhZ2VVdGlscy50b1Jhd01lc3NhZ2UoZGV2aWNlLCBtZXNzYWdlKTtcblxuICAgIGF3YWl0IHBlbmRpbmdNZXNzYWdlQ2FjaGVTdHViLmFkZChkZXZpY2UsIG1lc3NhZ2UpO1xuXG4gICAgY29uc3QgaW5pdGlhbENhY2hlID0gYXdhaXQgcGVuZGluZ01lc3NhZ2VDYWNoZVN0dWIuZ2V0QWxsUGVuZGluZygpO1xuICAgIGV4cGVjdChpbml0aWFsQ2FjaGUpLnRvLmhhdmUubGVuZ3RoKDEpO1xuXG4gICAgLy8gUmVtb3ZlIHRoZSBtZXNzYWdlXG4gICAgYXdhaXQgcGVuZGluZ01lc3NhZ2VDYWNoZVN0dWIucmVtb3ZlKHJhd01lc3NhZ2UpO1xuXG4gICAgY29uc3QgZmluYWxDYWNoZSA9IGF3YWl0IHBlbmRpbmdNZXNzYWdlQ2FjaGVTdHViLmdldEFsbFBlbmRpbmcoKTtcblxuICAgIC8vIFZlcmlmeSB0aGF0IHRoZSBtZXNzYWdlIHdhcyByZW1vdmVkXG4gICAgZXhwZWN0KGZpbmFsQ2FjaGUpLnRvLmhhdmUubGVuZ3RoKDApO1xuICB9KTtcblxuICBpdCgnc2hvdWxkIG9ubHkgcmVtb3ZlIG1lc3NhZ2VzIHdpdGggZGlmZmVyZW50IGlkZW50aWZpZXIgYW5kIGRldmljZScsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBkZXZpY2UgPSBUZXN0VXRpbHMuZ2VuZXJhdGVGYWtlUHViS2V5KCk7XG4gICAgY29uc3QgbWVzc2FnZSA9IFRlc3RVdGlscy5nZW5lcmF0ZVZpc2libGVNZXNzYWdlKCk7XG4gICAgY29uc3QgcmF3TWVzc2FnZSA9IGF3YWl0IE1lc3NhZ2VVdGlscy50b1Jhd01lc3NhZ2UoZGV2aWNlLCBtZXNzYWdlKTtcblxuICAgIGF3YWl0IHBlbmRpbmdNZXNzYWdlQ2FjaGVTdHViLmFkZChkZXZpY2UsIG1lc3NhZ2UpO1xuICAgIGF3YWl0IFRlc3RVdGlscy50aW1lb3V0KDUpO1xuICAgIGNvbnN0IG9uZSA9IGF3YWl0IHBlbmRpbmdNZXNzYWdlQ2FjaGVTdHViLmFkZChkZXZpY2UsIFRlc3RVdGlscy5nZW5lcmF0ZVZpc2libGVNZXNzYWdlKCkpO1xuICAgIGNvbnN0IHR3byA9IGF3YWl0IHBlbmRpbmdNZXNzYWdlQ2FjaGVTdHViLmFkZChUZXN0VXRpbHMuZ2VuZXJhdGVGYWtlUHViS2V5KCksIG1lc3NhZ2UpO1xuXG4gICAgY29uc3QgaW5pdGlhbENhY2hlID0gYXdhaXQgcGVuZGluZ01lc3NhZ2VDYWNoZVN0dWIuZ2V0QWxsUGVuZGluZygpO1xuICAgIGV4cGVjdChpbml0aWFsQ2FjaGUpLnRvLmhhdmUubGVuZ3RoKDMpO1xuXG4gICAgLy8gUmVtb3ZlIHRoZSBtZXNzYWdlXG4gICAgYXdhaXQgcGVuZGluZ01lc3NhZ2VDYWNoZVN0dWIucmVtb3ZlKHJhd01lc3NhZ2UpO1xuXG4gICAgY29uc3QgZmluYWxDYWNoZSA9IGF3YWl0IHBlbmRpbmdNZXNzYWdlQ2FjaGVTdHViLmdldEFsbFBlbmRpbmcoKTtcblxuICAgIC8vIFZlcmlmeSB0aGF0IHRoZSBtZXNzYWdlIHdhcyByZW1vdmVkXG4gICAgZXhwZWN0KGZpbmFsQ2FjaGUpLnRvLmhhdmUubGVuZ3RoKDIpO1xuICAgIGV4cGVjdChmaW5hbENhY2hlKS50by5oYXZlLmRlZXAubWVtYmVycyhbb25lLCB0d29dKTtcbiAgfSk7XG5cbiAgaXQoJ2NhbiBnZXQgZGV2aWNlcycsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBjYWNoZUl0ZW1zID0gW1xuICAgICAge1xuICAgICAgICBkZXZpY2U6IFRlc3RVdGlscy5nZW5lcmF0ZUZha2VQdWJLZXkoKSxcbiAgICAgICAgbWVzc2FnZTogVGVzdFV0aWxzLmdlbmVyYXRlVmlzaWJsZU1lc3NhZ2UoKSxcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIGRldmljZTogVGVzdFV0aWxzLmdlbmVyYXRlRmFrZVB1YktleSgpLFxuICAgICAgICBtZXNzYWdlOiBUZXN0VXRpbHMuZ2VuZXJhdGVWaXNpYmxlTWVzc2FnZSgpLFxuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgZGV2aWNlOiBUZXN0VXRpbHMuZ2VuZXJhdGVGYWtlUHViS2V5KCksXG4gICAgICAgIG1lc3NhZ2U6IFRlc3RVdGlscy5nZW5lcmF0ZVZpc2libGVNZXNzYWdlKCksXG4gICAgICB9LFxuICAgIF07XG5cbiAgICBmb3IgKGNvbnN0IGl0ZW0gb2YgY2FjaGVJdGVtcykge1xuICAgICAgYXdhaXQgcGVuZGluZ01lc3NhZ2VDYWNoZVN0dWIuYWRkKGl0ZW0uZGV2aWNlLCBpdGVtLm1lc3NhZ2UpO1xuICAgIH1cblxuICAgIGNvbnN0IGNhY2hlID0gYXdhaXQgcGVuZGluZ01lc3NhZ2VDYWNoZVN0dWIuZ2V0QWxsUGVuZGluZygpO1xuICAgIGV4cGVjdChjYWNoZSkudG8uaGF2ZS5sZW5ndGgoY2FjaGVJdGVtcy5sZW5ndGgpO1xuXG4gICAgLy8gR2V0IGxpc3Qgb2YgZGV2aWNlc1xuICAgIGNvbnN0IGRldmljZXNLZXlzID0gY2FjaGVJdGVtcy5tYXAoaXRlbSA9PiBpdGVtLmRldmljZS5rZXkpO1xuICAgIGNvbnN0IHB1bGxlZERldmljZXMgPSBhd2FpdCBwZW5kaW5nTWVzc2FnZUNhY2hlU3R1Yi5nZXREZXZpY2VzKCk7XG4gICAgY29uc3QgcHVsbGVkRGV2aWNlc0tleXMgPSBwdWxsZWREZXZpY2VzLm1hcChkID0+IGQua2V5KTtcblxuICAgIC8vIFZlcmlmeSB0aGF0IGRldmljZSBsaXN0IGZyb20gY2FjaGUgaXMgZXF1aXZhbGVudCB0byBkZXZpY2VzIGFkZGVkXG4gICAgZXhwZWN0KHB1bGxlZERldmljZXNLZXlzKS50by5oYXZlLm1lbWJlcnMoZGV2aWNlc0tleXMpO1xuICB9KTtcblxuICBpdCgnY2FuIGdldCBwZW5kaW5nIGZvciBkZXZpY2UnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgY2FjaGVJdGVtcyA9IFtcbiAgICAgIHtcbiAgICAgICAgZGV2aWNlOiBUZXN0VXRpbHMuZ2VuZXJhdGVGYWtlUHViS2V5KCksXG4gICAgICAgIG1lc3NhZ2U6IFRlc3RVdGlscy5nZW5lcmF0ZVZpc2libGVNZXNzYWdlKCksXG4gICAgICB9LFxuICAgICAge1xuICAgICAgICBkZXZpY2U6IFRlc3RVdGlscy5nZW5lcmF0ZUZha2VQdWJLZXkoKSxcbiAgICAgICAgbWVzc2FnZTogVGVzdFV0aWxzLmdlbmVyYXRlVmlzaWJsZU1lc3NhZ2UoKSxcbiAgICAgIH0sXG4gICAgXTtcblxuICAgIGZvciAoY29uc3QgaXRlbSBvZiBjYWNoZUl0ZW1zKSB7XG4gICAgICBhd2FpdCBwZW5kaW5nTWVzc2FnZUNhY2hlU3R1Yi5hZGQoaXRlbS5kZXZpY2UsIGl0ZW0ubWVzc2FnZSk7XG4gICAgfVxuXG4gICAgY29uc3QgaW5pdGlhbENhY2hlID0gYXdhaXQgcGVuZGluZ01lc3NhZ2VDYWNoZVN0dWIuZ2V0QWxsUGVuZGluZygpO1xuICAgIGV4cGVjdChpbml0aWFsQ2FjaGUpLnRvLmhhdmUubGVuZ3RoKGNhY2hlSXRlbXMubGVuZ3RoKTtcblxuICAgIC8vIEdldCBwZW5kaW5nIGZvciBlYWNoIHNwZWNpZmljIGRldmljZVxuICAgIGZvciAoY29uc3QgaXRlbSBvZiBjYWNoZUl0ZW1zKSB7XG4gICAgICBjb25zdCBwZW5kaW5nRm9yRGV2aWNlID0gYXdhaXQgcGVuZGluZ01lc3NhZ2VDYWNoZVN0dWIuZ2V0Rm9yRGV2aWNlKGl0ZW0uZGV2aWNlKTtcbiAgICAgIGV4cGVjdChwZW5kaW5nRm9yRGV2aWNlKS50by5oYXZlLmxlbmd0aCgxKTtcbiAgICAgIGV4cGVjdChwZW5kaW5nRm9yRGV2aWNlWzBdLmRldmljZSkudG8uZXF1YWwoaXRlbS5kZXZpY2Uua2V5KTtcbiAgICB9XG4gIH0pO1xuXG4gIGl0KCdjYW4gZmluZCBub3RoaW5nIHdoZW4gZW1wdHknLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgZGV2aWNlID0gVGVzdFV0aWxzLmdlbmVyYXRlRmFrZVB1YktleSgpO1xuICAgIGNvbnN0IG1lc3NhZ2UgPSBUZXN0VXRpbHMuZ2VuZXJhdGVWaXNpYmxlTWVzc2FnZSgpO1xuICAgIGNvbnN0IHJhd01lc3NhZ2UgPSBhd2FpdCBNZXNzYWdlVXRpbHMudG9SYXdNZXNzYWdlKGRldmljZSwgbWVzc2FnZSk7XG5cbiAgICBjb25zdCBmb3VuZE1lc3NhZ2UgPSBwZW5kaW5nTWVzc2FnZUNhY2hlU3R1Yi5maW5kKHJhd01lc3NhZ2UpO1xuICAgIGV4cGVjdChmb3VuZE1lc3NhZ2UsICdhIG1lc3NhZ2Ugd2FzIGZvdW5kIGluIGVtcHR5IGNhY2hlJykudG8uYmUudW5kZWZpbmVkO1xuICB9KTtcblxuICBpdCgnY2FuIGZpbmQgbWVzc2FnZSBpbiBjYWNoZScsIGFzeW5jICgpID0+IHtcbiAgICBjb25zdCBkZXZpY2UgPSBUZXN0VXRpbHMuZ2VuZXJhdGVGYWtlUHViS2V5KCk7XG4gICAgY29uc3QgbWVzc2FnZSA9IFRlc3RVdGlscy5nZW5lcmF0ZVZpc2libGVNZXNzYWdlKCk7XG4gICAgY29uc3QgcmF3TWVzc2FnZSA9IGF3YWl0IE1lc3NhZ2VVdGlscy50b1Jhd01lc3NhZ2UoZGV2aWNlLCBtZXNzYWdlKTtcblxuICAgIGF3YWl0IHBlbmRpbmdNZXNzYWdlQ2FjaGVTdHViLmFkZChkZXZpY2UsIG1lc3NhZ2UpO1xuXG4gICAgY29uc3QgZmluYWxDYWNoZSA9IGF3YWl0IHBlbmRpbmdNZXNzYWdlQ2FjaGVTdHViLmdldEFsbFBlbmRpbmcoKTtcbiAgICBleHBlY3QoZmluYWxDYWNoZSkudG8uaGF2ZS5sZW5ndGgoMSk7XG5cbiAgICBjb25zdCBmb3VuZE1lc3NhZ2UgPSBwZW5kaW5nTWVzc2FnZUNhY2hlU3R1Yi5maW5kKHJhd01lc3NhZ2UpO1xuICAgIGV4cGVjdChmb3VuZE1lc3NhZ2UsICdtZXNzYWdlIG5vdCBmb3VuZCBpbiBjYWNoZScpLnRvLmJlLm9rO1xuICAgIGZvdW5kTWVzc2FnZSAmJiBleHBlY3QoZm91bmRNZXNzYWdlLmRldmljZSkudG8uZXF1YWwoZGV2aWNlLmtleSk7XG4gIH0pO1xuXG4gIGl0KCdjYW4gY2xlYXIgY2FjaGUnLCBhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgY2FjaGVJdGVtcyA9IFtcbiAgICAgIHtcbiAgICAgICAgZGV2aWNlOiBUZXN0VXRpbHMuZ2VuZXJhdGVGYWtlUHViS2V5KCksXG4gICAgICAgIG1lc3NhZ2U6IFRlc3RVdGlscy5nZW5lcmF0ZVZpc2libGVNZXNzYWdlKCksXG4gICAgICB9LFxuICAgICAge1xuICAgICAgICBkZXZpY2U6IFRlc3RVdGlscy5nZW5lcmF0ZUZha2VQdWJLZXkoKSxcbiAgICAgICAgbWVzc2FnZTogVGVzdFV0aWxzLmdlbmVyYXRlVmlzaWJsZU1lc3NhZ2UoKSxcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIGRldmljZTogVGVzdFV0aWxzLmdlbmVyYXRlRmFrZVB1YktleSgpLFxuICAgICAgICBtZXNzYWdlOiBUZXN0VXRpbHMuZ2VuZXJhdGVWaXNpYmxlTWVzc2FnZSgpLFxuICAgICAgfSxcbiAgICBdO1xuXG4gICAgZm9yIChjb25zdCBpdGVtIG9mIGNhY2hlSXRlbXMpIHtcbiAgICAgIGF3YWl0IHBlbmRpbmdNZXNzYWdlQ2FjaGVTdHViLmFkZChpdGVtLmRldmljZSwgaXRlbS5tZXNzYWdlKTtcbiAgICB9XG5cbiAgICBjb25zdCBpbml0aWFsQ2FjaGUgPSBhd2FpdCBwZW5kaW5nTWVzc2FnZUNhY2hlU3R1Yi5nZXRBbGxQZW5kaW5nKCk7XG4gICAgZXhwZWN0KGluaXRpYWxDYWNoZSkudG8uaGF2ZS5sZW5ndGgoY2FjaGVJdGVtcy5sZW5ndGgpO1xuXG4gICAgLy8gQ2xlYXIgY2FjaGVcbiAgICBhd2FpdCBwZW5kaW5nTWVzc2FnZUNhY2hlU3R1Yi5jbGVhcigpO1xuXG4gICAgY29uc3QgZmluYWxDYWNoZSA9IGF3YWl0IHBlbmRpbmdNZXNzYWdlQ2FjaGVTdHViLmdldEFsbFBlbmRpbmcoKTtcbiAgICBleHBlY3QoZmluYWxDYWNoZSkudG8uaGF2ZS5sZW5ndGgoMCk7XG4gIH0pO1xuXG4gIGl0KCdjYW4gcmVzdG9yZSBmcm9tIGRiJywgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGNhY2hlSXRlbXMgPSBbXG4gICAgICB7XG4gICAgICAgIGRldmljZTogVGVzdFV0aWxzLmdlbmVyYXRlRmFrZVB1YktleSgpLFxuICAgICAgICBtZXNzYWdlOiBUZXN0VXRpbHMuZ2VuZXJhdGVWaXNpYmxlTWVzc2FnZSgpLFxuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgZGV2aWNlOiBUZXN0VXRpbHMuZ2VuZXJhdGVGYWtlUHViS2V5KCksXG4gICAgICAgIG1lc3NhZ2U6IFRlc3RVdGlscy5nZW5lcmF0ZVZpc2libGVNZXNzYWdlKCksXG4gICAgICB9LFxuICAgICAge1xuICAgICAgICBkZXZpY2U6IFRlc3RVdGlscy5nZW5lcmF0ZUZha2VQdWJLZXkoKSxcbiAgICAgICAgbWVzc2FnZTogVGVzdFV0aWxzLmdlbmVyYXRlVmlzaWJsZU1lc3NhZ2UoKSxcbiAgICAgIH0sXG4gICAgXTtcblxuICAgIGZvciAoY29uc3QgaXRlbSBvZiBjYWNoZUl0ZW1zKSB7XG4gICAgICBhd2FpdCBwZW5kaW5nTWVzc2FnZUNhY2hlU3R1Yi5hZGQoaXRlbS5kZXZpY2UsIGl0ZW0ubWVzc2FnZSk7XG4gICAgfVxuXG4gICAgY29uc3QgYWRkZWRNZXNzYWdlcyA9IGF3YWl0IHBlbmRpbmdNZXNzYWdlQ2FjaGVTdHViLmdldEFsbFBlbmRpbmcoKTtcbiAgICBleHBlY3QoYWRkZWRNZXNzYWdlcykudG8uaGF2ZS5sZW5ndGgoY2FjaGVJdGVtcy5sZW5ndGgpO1xuXG4gICAgLy8gUmVidWlsZCBmcm9tIERCXG4gICAgY29uc3QgZnJlc2hDYWNoZSA9IG5ldyBQZW5kaW5nTWVzc2FnZUNhY2hlKCk7XG5cbiAgICAvLyBWZXJpZnkgbWVzc2FnZXNcbiAgICBjb25zdCByZWJ1aWx0TWVzc2FnZXMgPSBhd2FpdCBmcmVzaENhY2hlLmdldEFsbFBlbmRpbmcoKTtcblxuICAgIGZvciAoY29uc3QgW2luZGV4LCBtZXNzYWdlXSBvZiByZWJ1aWx0TWVzc2FnZXMuZW50cmllcygpKSB7XG4gICAgICBjb25zdCBhZGRlZE1lc3NhZ2UgPSBhZGRlZE1lc3NhZ2VzW2luZGV4XTtcblxuICAgICAgLy8gUHVsbCBvdXQgcGxhaW5UZXh0QnVmZmVyIGZvciBhIHNlcGFyYXRlIGNoZWNrXG4gICAgICBjb25zdCBidWZmZXJzQ29tcGFyZSA9XG4gICAgICAgIEJ1ZmZlci5jb21wYXJlKG1lc3NhZ2UucGxhaW5UZXh0QnVmZmVyLCBhZGRlZE1lc3NhZ2UucGxhaW5UZXh0QnVmZmVyKSA9PT0gMDtcbiAgICAgIGV4cGVjdChidWZmZXJzQ29tcGFyZSkudG8uZXF1YWwodHJ1ZSwgJ2J1ZmZlcnMgd2VyZSBub3QgbG9hZGVkIHByb3Blcmx5IGZyb20gZGF0YWJhc2UnKTtcblxuICAgICAgLy8gQ29tcGFyZSBhbGwgb3RoZXIgdmFsdXJlc1xuICAgICAgY29uc3QgdHJpbW1lZEFkZGVkID0gXy5vbWl0KGFkZGVkTWVzc2FnZSwgWydwbGFpblRleHRCdWZmZXInXSk7XG4gICAgICBjb25zdCB0cmltbWVkUmVidWlsdCA9IF8ub21pdChtZXNzYWdlLCBbJ3BsYWluVGV4dEJ1ZmZlciddKTtcblxuICAgICAgZXhwZWN0KF8uaXNFcXVhbCh0cmltbWVkQWRkZWQsIHRyaW1tZWRSZWJ1aWx0KSkudG8uZXF1YWwoXG4gICAgICAgIHRydWUsXG4gICAgICAgICdjYWNoZWQgbWVzc2FnZXMgd2VyZSBub3QgcmVidWlsdCBwcm9wZXJseSdcbiAgICAgICk7XG4gICAgfVxuICB9KTtcbn0pO1xuIl0sCiAgIm1hcHBpbmdzIjogIjs7Ozs7Ozs7Ozs7Ozs7O0FBQ0Esa0JBQXVCO0FBQ3ZCLFlBQXVCO0FBQ3ZCLFFBQW1CO0FBQ25CLG1CQUE2QjtBQUM3Qix3QkFBMEI7QUFDMUIsaUNBQW9DO0FBUXBDLFNBQVMsdUJBQXVCLE1BQU07QUFDcEMsUUFBTSxVQUFVLE1BQU0sY0FBYztBQUVwQyxNQUFJO0FBQ0osTUFBSTtBQUVKLGFBQVcsTUFBTTtBQUVmLFVBQU0sWUFBWTtBQUNsQixXQUFPO0FBQUEsTUFDTCxJQUFJO0FBQUEsTUFDSixPQUFPO0FBQUEsSUFDVDtBQUVBLGdDQUFVLFNBQVMsYUFBYSxFQUM3QixTQUFTLGlCQUFpQixFQUMxQixVQUFVLFlBQVk7QUFDckIsYUFBTztBQUFBLElBQ1QsQ0FBQztBQUVILGdDQUFVLFNBQVMsb0JBQW9CLEVBQUUsVUFBVSxDQUFDLFNBQXNCO0FBQ3hFLFVBQUksS0FBSyxPQUFPLFdBQVc7QUFDekIsZUFBTztBQUFBLE1BQ1Q7QUFBQSxJQUNGLENBQUM7QUFFRCw4QkFBMEIsSUFBSSwrQ0FBb0I7QUFBQSxFQUNwRCxDQUFDO0FBRUQsWUFBVSxNQUFNO0FBQ2QsWUFBUSxRQUFRO0FBQ2hCLGdDQUFVLGFBQWE7QUFBQSxFQUN6QixDQUFDO0FBRUQsS0FBRyx3QkFBd0IsWUFBWTtBQUNyQyxVQUFNLFFBQVEsTUFBTSx3QkFBd0IsY0FBYztBQUcxRCw0QkFBTyxLQUFLLEVBQUUsR0FBRyxHQUFHLFdBQVcsS0FBSztBQUNwQyw0QkFBTyxLQUFLLEVBQUUsR0FBRyxLQUFLLE9BQU8sQ0FBQztBQUFBLEVBQ2hDLENBQUM7QUFFRCxLQUFHLG9CQUFvQixZQUFZO0FBQ2pDLFVBQU0sU0FBUyw0QkFBVSxtQkFBbUI7QUFDNUMsVUFBTSxVQUFVLDRCQUFVLHVCQUF1QjtBQUNqRCxVQUFNLGFBQWEsTUFBTSwwQkFBYSxhQUFhLFFBQVEsT0FBTztBQUVsRSxVQUFNLHdCQUF3QixJQUFJLFFBQVEsT0FBTztBQUdqRCxVQUFNLGFBQWEsTUFBTSx3QkFBd0IsY0FBYztBQUUvRCw0QkFBTyxVQUFVLEVBQUUsR0FBRyxLQUFLLE9BQU8sQ0FBQztBQUVuQyxVQUFNLGVBQWUsV0FBVztBQUNoQyw0QkFBTyxhQUFhLE1BQU0sRUFBRSxHQUFHLEtBQUssTUFBTSxXQUFXLE1BQU07QUFBQSxFQUM3RCxDQUFDO0FBRUQsS0FBRyx3REFBd0QsWUFBWTtBQUNyRSxVQUFNLFNBQVMsNEJBQVUsbUJBQW1CO0FBRTVDLFVBQU0sd0JBQXdCLElBQUksUUFBUSw0QkFBVSx1QkFBdUIsQ0FBQztBQUU1RSxVQUFNLDRCQUFVLFFBQVEsQ0FBQztBQUN6QixVQUFNLHdCQUF3QixJQUFJLFFBQVEsNEJBQVUsdUJBQXVCLENBQUM7QUFDNUUsVUFBTSw0QkFBVSxRQUFRLENBQUM7QUFDekIsVUFBTSx3QkFBd0IsSUFBSSxRQUFRLDRCQUFVLHVCQUF1QixDQUFDO0FBRzVFLFVBQU0sYUFBYSxNQUFNLHdCQUF3QixjQUFjO0FBRS9ELDRCQUFPLFVBQVUsRUFBRSxHQUFHLEtBQUssT0FBTyxDQUFDO0FBQUEsRUFDckMsQ0FBQztBQUVELEtBQUcseUJBQXlCLFlBQVk7QUFDdEMsVUFBTSxTQUFTLDRCQUFVLG1CQUFtQjtBQUM1QyxVQUFNLFVBQVUsNEJBQVUsdUJBQXVCO0FBQ2pELFVBQU0sYUFBYSxNQUFNLDBCQUFhLGFBQWEsUUFBUSxPQUFPO0FBRWxFLFVBQU0sd0JBQXdCLElBQUksUUFBUSxPQUFPO0FBRWpELFVBQU0sZUFBZSxNQUFNLHdCQUF3QixjQUFjO0FBQ2pFLDRCQUFPLFlBQVksRUFBRSxHQUFHLEtBQUssT0FBTyxDQUFDO0FBR3JDLFVBQU0sd0JBQXdCLE9BQU8sVUFBVTtBQUUvQyxVQUFNLGFBQWEsTUFBTSx3QkFBd0IsY0FBYztBQUcvRCw0QkFBTyxVQUFVLEVBQUUsR0FBRyxLQUFLLE9BQU8sQ0FBQztBQUFBLEVBQ3JDLENBQUM7QUFFRCxLQUFHLG9FQUFvRSxZQUFZO0FBQ2pGLFVBQU0sU0FBUyw0QkFBVSxtQkFBbUI7QUFDNUMsVUFBTSxVQUFVLDRCQUFVLHVCQUF1QjtBQUNqRCxVQUFNLGFBQWEsTUFBTSwwQkFBYSxhQUFhLFFBQVEsT0FBTztBQUVsRSxVQUFNLHdCQUF3QixJQUFJLFFBQVEsT0FBTztBQUNqRCxVQUFNLDRCQUFVLFFBQVEsQ0FBQztBQUN6QixVQUFNLE1BQU0sTUFBTSx3QkFBd0IsSUFBSSxRQUFRLDRCQUFVLHVCQUF1QixDQUFDO0FBQ3hGLFVBQU0sTUFBTSxNQUFNLHdCQUF3QixJQUFJLDRCQUFVLG1CQUFtQixHQUFHLE9BQU87QUFFckYsVUFBTSxlQUFlLE1BQU0sd0JBQXdCLGNBQWM7QUFDakUsNEJBQU8sWUFBWSxFQUFFLEdBQUcsS0FBSyxPQUFPLENBQUM7QUFHckMsVUFBTSx3QkFBd0IsT0FBTyxVQUFVO0FBRS9DLFVBQU0sYUFBYSxNQUFNLHdCQUF3QixjQUFjO0FBRy9ELDRCQUFPLFVBQVUsRUFBRSxHQUFHLEtBQUssT0FBTyxDQUFDO0FBQ25DLDRCQUFPLFVBQVUsRUFBRSxHQUFHLEtBQUssS0FBSyxRQUFRLENBQUMsS0FBSyxHQUFHLENBQUM7QUFBQSxFQUNwRCxDQUFDO0FBRUQsS0FBRyxtQkFBbUIsWUFBWTtBQUNoQyxVQUFNLGFBQWE7QUFBQSxNQUNqQjtBQUFBLFFBQ0UsUUFBUSw0QkFBVSxtQkFBbUI7QUFBQSxRQUNyQyxTQUFTLDRCQUFVLHVCQUF1QjtBQUFBLE1BQzVDO0FBQUEsTUFDQTtBQUFBLFFBQ0UsUUFBUSw0QkFBVSxtQkFBbUI7QUFBQSxRQUNyQyxTQUFTLDRCQUFVLHVCQUF1QjtBQUFBLE1BQzVDO0FBQUEsTUFDQTtBQUFBLFFBQ0UsUUFBUSw0QkFBVSxtQkFBbUI7QUFBQSxRQUNyQyxTQUFTLDRCQUFVLHVCQUF1QjtBQUFBLE1BQzVDO0FBQUEsSUFDRjtBQUVBLGVBQVcsUUFBUSxZQUFZO0FBQzdCLFlBQU0sd0JBQXdCLElBQUksS0FBSyxRQUFRLEtBQUssT0FBTztBQUFBLElBQzdEO0FBRUEsVUFBTSxRQUFRLE1BQU0sd0JBQXdCLGNBQWM7QUFDMUQsNEJBQU8sS0FBSyxFQUFFLEdBQUcsS0FBSyxPQUFPLFdBQVcsTUFBTTtBQUc5QyxVQUFNLGNBQWMsV0FBVyxJQUFJLFVBQVEsS0FBSyxPQUFPLEdBQUc7QUFDMUQsVUFBTSxnQkFBZ0IsTUFBTSx3QkFBd0IsV0FBVztBQUMvRCxVQUFNLG9CQUFvQixjQUFjLElBQUksT0FBSyxFQUFFLEdBQUc7QUFHdEQsNEJBQU8saUJBQWlCLEVBQUUsR0FBRyxLQUFLLFFBQVEsV0FBVztBQUFBLEVBQ3ZELENBQUM7QUFFRCxLQUFHLDhCQUE4QixZQUFZO0FBQzNDLFVBQU0sYUFBYTtBQUFBLE1BQ2pCO0FBQUEsUUFDRSxRQUFRLDRCQUFVLG1CQUFtQjtBQUFBLFFBQ3JDLFNBQVMsNEJBQVUsdUJBQXVCO0FBQUEsTUFDNUM7QUFBQSxNQUNBO0FBQUEsUUFDRSxRQUFRLDRCQUFVLG1CQUFtQjtBQUFBLFFBQ3JDLFNBQVMsNEJBQVUsdUJBQXVCO0FBQUEsTUFDNUM7QUFBQSxJQUNGO0FBRUEsZUFBVyxRQUFRLFlBQVk7QUFDN0IsWUFBTSx3QkFBd0IsSUFBSSxLQUFLLFFBQVEsS0FBSyxPQUFPO0FBQUEsSUFDN0Q7QUFFQSxVQUFNLGVBQWUsTUFBTSx3QkFBd0IsY0FBYztBQUNqRSw0QkFBTyxZQUFZLEVBQUUsR0FBRyxLQUFLLE9BQU8sV0FBVyxNQUFNO0FBR3JELGVBQVcsUUFBUSxZQUFZO0FBQzdCLFlBQU0sbUJBQW1CLE1BQU0sd0JBQXdCLGFBQWEsS0FBSyxNQUFNO0FBQy9FLDhCQUFPLGdCQUFnQixFQUFFLEdBQUcsS0FBSyxPQUFPLENBQUM7QUFDekMsOEJBQU8saUJBQWlCLEdBQUcsTUFBTSxFQUFFLEdBQUcsTUFBTSxLQUFLLE9BQU8sR0FBRztBQUFBLElBQzdEO0FBQUEsRUFDRixDQUFDO0FBRUQsS0FBRywrQkFBK0IsWUFBWTtBQUM1QyxVQUFNLFNBQVMsNEJBQVUsbUJBQW1CO0FBQzVDLFVBQU0sVUFBVSw0QkFBVSx1QkFBdUI7QUFDakQsVUFBTSxhQUFhLE1BQU0sMEJBQWEsYUFBYSxRQUFRLE9BQU87QUFFbEUsVUFBTSxlQUFlLHdCQUF3QixLQUFLLFVBQVU7QUFDNUQsNEJBQU8sY0FBYyxvQ0FBb0MsRUFBRSxHQUFHLEdBQUc7QUFBQSxFQUNuRSxDQUFDO0FBRUQsS0FBRyw2QkFBNkIsWUFBWTtBQUMxQyxVQUFNLFNBQVMsNEJBQVUsbUJBQW1CO0FBQzVDLFVBQU0sVUFBVSw0QkFBVSx1QkFBdUI7QUFDakQsVUFBTSxhQUFhLE1BQU0sMEJBQWEsYUFBYSxRQUFRLE9BQU87QUFFbEUsVUFBTSx3QkFBd0IsSUFBSSxRQUFRLE9BQU87QUFFakQsVUFBTSxhQUFhLE1BQU0sd0JBQXdCLGNBQWM7QUFDL0QsNEJBQU8sVUFBVSxFQUFFLEdBQUcsS0FBSyxPQUFPLENBQUM7QUFFbkMsVUFBTSxlQUFlLHdCQUF3QixLQUFLLFVBQVU7QUFDNUQsNEJBQU8sY0FBYyw0QkFBNEIsRUFBRSxHQUFHLEdBQUc7QUFDekQsb0JBQWdCLHdCQUFPLGFBQWEsTUFBTSxFQUFFLEdBQUcsTUFBTSxPQUFPLEdBQUc7QUFBQSxFQUNqRSxDQUFDO0FBRUQsS0FBRyxtQkFBbUIsWUFBWTtBQUNoQyxVQUFNLGFBQWE7QUFBQSxNQUNqQjtBQUFBLFFBQ0UsUUFBUSw0QkFBVSxtQkFBbUI7QUFBQSxRQUNyQyxTQUFTLDRCQUFVLHVCQUF1QjtBQUFBLE1BQzVDO0FBQUEsTUFDQTtBQUFBLFFBQ0UsUUFBUSw0QkFBVSxtQkFBbUI7QUFBQSxRQUNyQyxTQUFTLDRCQUFVLHVCQUF1QjtBQUFBLE1BQzVDO0FBQUEsTUFDQTtBQUFBLFFBQ0UsUUFBUSw0QkFBVSxtQkFBbUI7QUFBQSxRQUNyQyxTQUFTLDRCQUFVLHVCQUF1QjtBQUFBLE1BQzVDO0FBQUEsSUFDRjtBQUVBLGVBQVcsUUFBUSxZQUFZO0FBQzdCLFlBQU0sd0JBQXdCLElBQUksS0FBSyxRQUFRLEtBQUssT0FBTztBQUFBLElBQzdEO0FBRUEsVUFBTSxlQUFlLE1BQU0sd0JBQXdCLGNBQWM7QUFDakUsNEJBQU8sWUFBWSxFQUFFLEdBQUcsS0FBSyxPQUFPLFdBQVcsTUFBTTtBQUdyRCxVQUFNLHdCQUF3QixNQUFNO0FBRXBDLFVBQU0sYUFBYSxNQUFNLHdCQUF3QixjQUFjO0FBQy9ELDRCQUFPLFVBQVUsRUFBRSxHQUFHLEtBQUssT0FBTyxDQUFDO0FBQUEsRUFDckMsQ0FBQztBQUVELEtBQUcsdUJBQXVCLFlBQVk7QUFDcEMsVUFBTSxhQUFhO0FBQUEsTUFDakI7QUFBQSxRQUNFLFFBQVEsNEJBQVUsbUJBQW1CO0FBQUEsUUFDckMsU0FBUyw0QkFBVSx1QkFBdUI7QUFBQSxNQUM1QztBQUFBLE1BQ0E7QUFBQSxRQUNFLFFBQVEsNEJBQVUsbUJBQW1CO0FBQUEsUUFDckMsU0FBUyw0QkFBVSx1QkFBdUI7QUFBQSxNQUM1QztBQUFBLE1BQ0E7QUFBQSxRQUNFLFFBQVEsNEJBQVUsbUJBQW1CO0FBQUEsUUFDckMsU0FBUyw0QkFBVSx1QkFBdUI7QUFBQSxNQUM1QztBQUFBLElBQ0Y7QUFFQSxlQUFXLFFBQVEsWUFBWTtBQUM3QixZQUFNLHdCQUF3QixJQUFJLEtBQUssUUFBUSxLQUFLLE9BQU87QUFBQSxJQUM3RDtBQUVBLFVBQU0sZ0JBQWdCLE1BQU0sd0JBQXdCLGNBQWM7QUFDbEUsNEJBQU8sYUFBYSxFQUFFLEdBQUcsS0FBSyxPQUFPLFdBQVcsTUFBTTtBQUd0RCxVQUFNLGFBQWEsSUFBSSwrQ0FBb0I7QUFHM0MsVUFBTSxrQkFBa0IsTUFBTSxXQUFXLGNBQWM7QUFFdkQsZUFBVyxDQUFDLE9BQU8sWUFBWSxnQkFBZ0IsUUFBUSxHQUFHO0FBQ3hELFlBQU0sZUFBZSxjQUFjO0FBR25DLFlBQU0saUJBQ0osT0FBTyxRQUFRLFFBQVEsaUJBQWlCLGFBQWEsZUFBZSxNQUFNO0FBQzVFLDhCQUFPLGNBQWMsRUFBRSxHQUFHLE1BQU0sTUFBTSxnREFBZ0Q7QUFHdEYsWUFBTSxlQUFlLEVBQUUsS0FBSyxjQUFjLENBQUMsaUJBQWlCLENBQUM7QUFDN0QsWUFBTSxpQkFBaUIsRUFBRSxLQUFLLFNBQVMsQ0FBQyxpQkFBaUIsQ0FBQztBQUUxRCw4QkFBTyxFQUFFLFFBQVEsY0FBYyxjQUFjLENBQUMsRUFBRSxHQUFHLE1BQ2pELE1BQ0EsMkNBQ0Y7QUFBQSxJQUNGO0FBQUEsRUFDRixDQUFDO0FBQ0gsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K