ddb-table
Version:
Strongly typed library for querying and modeling DynamoDB documents.
609 lines (607 loc) • 89.6 kB
JavaScript
"use strict";
require("mocha");
var _chai = require("chai");
var _ExpressionAttributeNames = _interopRequireDefault(require("./ExpressionAttributeNames"));
var _ConditionExpression = _interopRequireDefault(require("./ConditionExpression"));
var _ExpressionAttributeValues = _interopRequireDefault(require("./ExpressionAttributeValues"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
describe('ConditionExpression', () => {
describe('.getAttributeTypeString', () => {
it('String', () => {
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString('S'), 'S');
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString(String), 'S');
// Custom strings throws on type checks but returned as is
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString('test'), 'test');
});
it('String Set', () => {
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString('SS'), 'SS');
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString([String]), 'SS');
_chai.assert.throws(() => _ConditionExpression.default.getAttributeTypeString(['string']));
});
it('Number', () => {
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString('N'), 'N');
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString(Number), 'N');
_chai.assert.throws(() => _ConditionExpression.default.getAttributeTypeString(2));
});
it('Number Set', () => {
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString('NS'), 'NS');
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString([Number]), 'NS');
});
it('Binary', () => {
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString('B'), 'B');
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString(Buffer), 'B');
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString(ArrayBuffer), 'B');
});
it('Buffer Set', () => {
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString('BS'), 'BS');
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString([Buffer]), 'BS');
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString([ArrayBuffer]), 'BS');
});
it('Boolean', () => {
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString('BOOL'), 'BOOL');
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString(Boolean), 'BOOL');
_chai.assert.throws(() => _ConditionExpression.default.getAttributeTypeString(true));
});
it('Null', () => {
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString('NULL'), 'NULL');
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString(null), 'NULL');
_chai.assert.throws(() => _ConditionExpression.default.getAttributeTypeString(undefined));
});
it('List', () => {
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString('L'), 'L');
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString(Array), 'L');
_chai.assert.throws(() => _ConditionExpression.default.getAttributeTypeString([]));
});
it('Map', () => {
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString('M'), 'M');
_chai.assert.equal(_ConditionExpression.default.getAttributeTypeString(Object), 'M');
_chai.assert.throws(() => _ConditionExpression.default.getAttributeTypeString({}));
});
});
describe('init', () => {
it('from other expression', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values, '#foo = :foo');
const exp2 = new _ConditionExpression.default(names, values, exp);
_chai.assert.equal(exp.serialize(), exp2.serialize());
});
});
describe('init -> serialize()', () => {
function assetSerialize(test, expected = test.trim().replace(/\s+/g, ' ')) {
it(test, () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values, test);
_chai.assert.equal(exp.serialize(), expected);
});
}
assetSerialize('#foo = :foo');
assetSerialize(':foo = #foo');
assetSerialize('#foo = :foo AND #bar <> :bar');
assetSerialize('#foo = :foo OR #bar > :bar');
assetSerialize('#foo < :foo AND #bar >= :bar OR #zoo = :zoo');
});
describe('.name', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
_chai.assert.equal(exp.name('foo'), '#foo');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), undefined);
});
it('inner path', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
_chai.assert.equal(exp.name('foo', 'bar'), '#foo.#bar');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo',
'#bar': 'bar'
});
_chai.assert.deepEqual(values.serialize(), undefined);
});
});
describe('.value', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
_chai.assert.equal(exp.value('foo', 1), ':foo');
_chai.assert.deepEqual(names.serialize(), undefined);
_chai.assert.deepEqual(values.serialize(), {
':foo': 1
});
});
it('duplicate name', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
_chai.assert.equal(exp.value('foo', 1), ':foo');
_chai.assert.equal(exp.value('foo', 2), ':foo2');
_chai.assert.deepEqual(names.serialize(), undefined);
_chai.assert.deepEqual(values.serialize(), {
':foo': 1,
':foo2': 2
});
});
});
describe('.eq', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.eq('foo', 1);
_chai.assert.equal(exp.serialize(), '#foo = :foo');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 1
});
});
});
describe('.nq', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.nq('foo', 1);
_chai.assert.equal(exp.serialize(), '#foo <> :foo');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 1
});
});
});
describe('.lt', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.lt('foo', 1);
_chai.assert.equal(exp.serialize(), '#foo < :foo');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 1
});
});
});
describe('.lte', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.lte('foo', 1);
_chai.assert.equal(exp.serialize(), '#foo <= :foo');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 1
});
});
});
describe('.gt', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.gt('foo', 1);
_chai.assert.equal(exp.serialize(), '#foo > :foo');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 1
});
});
});
describe('.gte', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.gte('foo', 1);
_chai.assert.equal(exp.serialize(), '#foo >= :foo');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 1
});
});
});
describe('comparator', () => {
it('foo <> 1', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.comparator('<>', 'foo', 1);
_chai.assert.equal(exp.serialize(), '#foo <> :foo');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 1
});
});
it('foo.bar = 1', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.comparator('<>', ['foo', 'bar'], 1);
_chai.assert.equal(exp.serialize(), '#foo.#bar <> :bar');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo',
'#bar': 'bar'
});
_chai.assert.deepEqual(values.serialize(), {
':bar': 1
});
});
it('foo <> function', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.comparator('<>', ['foo'], (path, cn) => cn.fn('size', path));
_chai.assert.equal(exp.serialize(), '#foo <> size(#foo)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), undefined);
});
});
describe('.between', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.between('foo', 1, 2);
_chai.assert.equal(exp.serialize(), '#foo BETWEEN :foo AND :foo2');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 1,
':foo2': 2
});
});
it('inner key', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.between(['foo', 'bar'], 1, 2);
_chai.assert.equal(exp.serialize(), '#foo.#bar BETWEEN :bar AND :bar2');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo',
'#bar': 'bar'
});
_chai.assert.deepEqual(values.serialize(), {
':bar': 1,
':bar2': 2
});
});
});
describe('.attributeExists', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.attributeExists('foo');
_chai.assert.equal(exp.serialize(), 'attribute_exists(#foo)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), undefined);
});
it('inner path', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.attributeExists(['foo', 'bar']);
_chai.assert.equal(exp.serialize(), 'attribute_exists(#foo.#bar)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo',
'#bar': 'bar'
});
_chai.assert.deepEqual(values.serialize(), undefined);
});
});
describe('.attributeNotExists', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.attributeNotExists('foo');
_chai.assert.equal(exp.serialize(), 'attribute_not_exists(#foo)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), undefined);
});
it('inner path', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.attributeNotExists(['foo', 'bar']);
_chai.assert.equal(exp.serialize(), 'attribute_not_exists(#foo.#bar)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo',
'#bar': 'bar'
});
_chai.assert.deepEqual(values.serialize(), undefined);
});
});
describe('.attributeType', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.attributeType('foo', Number);
_chai.assert.equal(exp.serialize(), 'attribute_type(#foo, :foo)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 'N'
});
});
it('inner path', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.attributeType(['foo', 'bar'], [String]);
_chai.assert.equal(exp.serialize(), 'attribute_type(#foo.#bar, :bar)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo',
'#bar': 'bar'
});
_chai.assert.deepEqual(values.serialize(), {
':bar': 'SS'
});
});
});
describe('.beginsWith', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.beginsWith('foo', 'str');
_chai.assert.equal(exp.serialize(), 'begins_with(#foo, :foo)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 'str'
});
});
it('inner path', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.beginsWith(['foo', 'bar'], 'str2');
_chai.assert.equal(exp.serialize(), 'begins_with(#foo.#bar, :bar)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo',
'#bar': 'bar'
});
_chai.assert.deepEqual(values.serialize(), {
':bar': 'str2'
});
});
});
describe('.contains', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.contains('foo', 'str');
_chai.assert.equal(exp.serialize(), 'contains(#foo, :foo)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 'str'
});
});
it('string set use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.contains('foo', 'str');
_chai.assert.equal(exp.serialize(), 'contains(#foo, :foo)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 'str'
});
});
it('number set use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.contains('foo', 123);
_chai.assert.equal(exp.serialize(), 'contains(#foo, :foo)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 123
});
});
it('nullable string set use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.contains('foo', 'str');
_chai.assert.equal(exp.serialize(), 'contains(#foo, :foo)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 'str'
});
});
it('nullable number set use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.contains('foo', 3);
_chai.assert.equal(exp.serialize(), 'contains(#foo, :foo)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 3
});
});
it('other path', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.contains('foo', cn => cn.name('bar'));
_chai.assert.equal(exp.serialize(), 'contains(#foo, #bar)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo',
'#bar': 'bar'
});
_chai.assert.deepEqual(values.serialize(), undefined);
});
it('inner path', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.contains(['foo', 'bar'], 'str2');
_chai.assert.equal(exp.serialize(), 'contains(#foo.#bar, :bar)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo',
'#bar': 'bar'
});
_chai.assert.deepEqual(values.serialize(), {
':bar': 'str2'
});
});
});
describe('.size', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.size('foo');
_chai.assert.equal(exp.serialize(), 'size(#foo)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), undefined);
});
it('inner path', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.size(['foo', 'bar']);
_chai.assert.equal(exp.serialize(), 'size(#foo.#bar)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo',
'#bar': 'bar'
});
_chai.assert.deepEqual(values.serialize(), undefined);
});
});
describe('.and', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.and(cn => cn.attributeExists('foo'));
exp.and(cn => cn.eq('foo', 1));
_chai.assert.equal(exp.serialize(), 'attribute_exists(#foo) AND #foo = :foo');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 1
});
});
});
describe('.or', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.or(cn => cn.attributeExists('foo'));
exp.or(cn => cn.eq('foo', 1));
_chai.assert.equal(exp.serialize(), 'attribute_exists(#foo) OR #foo = :foo');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 1
});
});
it('inner AND', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.or(cn => cn.attributeExists('foo'));
exp.or(cn => cn.gt('foo', 1).and(cn => cn.lt('foo', 10)));
_chai.assert.equal(exp.serialize(), 'attribute_exists(#foo) OR (#foo > :foo AND #foo < :foo2)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 1,
':foo2': 10
});
});
it('freestyle string', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.or(cn => cn.attributeExists('foo'));
exp.or(() => '#foo = #foo');
_chai.assert.equal(exp.serialize(), 'attribute_exists(#foo) OR #foo = #foo');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), undefined);
});
});
describe('.not', () => {
it('basic use case', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.not(cn => cn.attributeExists('foo'));
_chai.assert.equal(exp.serialize(), 'NOT attribute_exists(#foo)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), undefined);
});
it('inner AND', () => {
const names = new _ExpressionAttributeNames.default();
const values = new _ExpressionAttributeValues.default();
const exp = new _ConditionExpression.default(names, values);
exp.not(cn => cn.attributeExists('foo'));
exp.or(cn => cn.gt('foo', 1).not(cn => cn.lt('foo', 10)));
_chai.assert.equal(exp.serialize(), 'NOT attribute_exists(#foo) OR (#foo > :foo AND NOT #foo < :foo2)');
_chai.assert.deepEqual(names.serialize(), {
'#foo': 'foo'
});
_chai.assert.deepEqual(values.serialize(), {
':foo': 1,
':foo2': 10
});
});
});
});
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJyZXF1aXJlIiwiX2NoYWkiLCJfRXhwcmVzc2lvbkF0dHJpYnV0ZU5hbWVzIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsIl9Db25kaXRpb25FeHByZXNzaW9uIiwiX0V4cHJlc3Npb25BdHRyaWJ1dGVWYWx1ZXMiLCJvYmoiLCJfX2VzTW9kdWxlIiwiZGVmYXVsdCIsImRlc2NyaWJlIiwiaXQiLCJhc3NlcnQiLCJlcXVhbCIsIkNvbmRpdGlvbkV4cHJlc3Npb24iLCJnZXRBdHRyaWJ1dGVUeXBlU3RyaW5nIiwiU3RyaW5nIiwidGhyb3dzIiwiTnVtYmVyIiwiQnVmZmVyIiwiQXJyYXlCdWZmZXIiLCJCb29sZWFuIiwidW5kZWZpbmVkIiwiQXJyYXkiLCJPYmplY3QiLCJuYW1lcyIsIkV4cHJlc3Npb25BdHRyaWJ1dGVOYW1lcyIsInZhbHVlcyIsIkV4cHJlc3Npb25BdHRyaWJ1dGVWYWx1ZXMiLCJleHAiLCJleHAyIiwic2VyaWFsaXplIiwiYXNzZXRTZXJpYWxpemUiLCJ0ZXN0IiwiZXhwZWN0ZWQiLCJ0cmltIiwicmVwbGFjZSIsIm5hbWUiLCJkZWVwRXF1YWwiLCJ2YWx1ZSIsImVxIiwibnEiLCJsdCIsImx0ZSIsImd0IiwiZ3RlIiwiY29tcGFyYXRvciIsInBhdGgiLCJjbiIsImZuIiwiYmV0d2VlbiIsImF0dHJpYnV0ZUV4aXN0cyIsImF0dHJpYnV0ZU5vdEV4aXN0cyIsImF0dHJpYnV0ZVR5cGUiLCJiZWdpbnNXaXRoIiwiY29udGFpbnMiLCJzaXplIiwiYW5kIiwib3IiLCJub3QiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvZXhwcmVzc2lvbnMvQ29uZGl0aW9uRXhwcmVzc2lvbi50ZXN0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAnbW9jaGEnO1xuaW1wb3J0IHsgYXNzZXJ0IH0gZnJvbSAnY2hhaSc7XG5pbXBvcnQgRXhwcmVzc2lvbkF0dHJpYnV0ZU5hbWVzIGZyb20gJy4vRXhwcmVzc2lvbkF0dHJpYnV0ZU5hbWVzJztcbmltcG9ydCBDb25kaXRpb25FeHByZXNzaW9uIGZyb20gJy4vQ29uZGl0aW9uRXhwcmVzc2lvbic7XG5pbXBvcnQgRXhwcmVzc2lvbkF0dHJpYnV0ZVZhbHVlcyBmcm9tICcuL0V4cHJlc3Npb25BdHRyaWJ1dGVWYWx1ZXMnO1xuXG5kZXNjcmliZSgnQ29uZGl0aW9uRXhwcmVzc2lvbicsICgpID0+IHtcbiAgZGVzY3JpYmUoJy5nZXRBdHRyaWJ1dGVUeXBlU3RyaW5nJywgKCkgPT4ge1xuICAgIGl0KCdTdHJpbmcnLCAoKSA9PiB7XG4gICAgICBhc3NlcnQuZXF1YWwoQ29uZGl0aW9uRXhwcmVzc2lvbi5nZXRBdHRyaWJ1dGVUeXBlU3RyaW5nKCdTJyksICdTJyk7XG4gICAgICBhc3NlcnQuZXF1YWwoQ29uZGl0aW9uRXhwcmVzc2lvbi5nZXRBdHRyaWJ1dGVUeXBlU3RyaW5nKFN0cmluZyksICdTJyk7XG5cbiAgICAgIC8vIEN1c3RvbSBzdHJpbmdzIHRocm93cyBvbiB0eXBlIGNoZWNrcyBidXQgcmV0dXJuZWQgYXMgaXNcbiAgICAgIGFzc2VydC5lcXVhbChcbiAgICAgICAgQ29uZGl0aW9uRXhwcmVzc2lvbi5nZXRBdHRyaWJ1dGVUeXBlU3RyaW5nKCd0ZXN0JyBhcyBuZXZlciksXG4gICAgICAgICd0ZXN0JyxcbiAgICAgICk7XG4gICAgfSk7XG5cbiAgICBpdCgnU3RyaW5nIFNldCcsICgpID0+IHtcbiAgICAgIGFzc2VydC5lcXVhbChDb25kaXRpb25FeHByZXNzaW9uLmdldEF0dHJpYnV0ZVR5cGVTdHJpbmcoJ1NTJyksICdTUycpO1xuICAgICAgYXNzZXJ0LmVxdWFsKENvbmRpdGlvbkV4cHJlc3Npb24uZ2V0QXR0cmlidXRlVHlwZVN0cmluZyhbU3RyaW5nXSksICdTUycpO1xuXG4gICAgICBhc3NlcnQudGhyb3dzKCgpID0+XG4gICAgICAgIENvbmRpdGlvbkV4cHJlc3Npb24uZ2V0QXR0cmlidXRlVHlwZVN0cmluZyhbJ3N0cmluZyddIGFzIG5ldmVyKSxcbiAgICAgICk7XG4gICAgfSk7XG5cbiAgICBpdCgnTnVtYmVyJywgKCkgPT4ge1xuICAgICAgYXNzZXJ0LmVxdWFsKENvbmRpdGlvbkV4cHJlc3Npb24uZ2V0QXR0cmlidXRlVHlwZVN0cmluZygnTicpLCAnTicpO1xuICAgICAgYXNzZXJ0LmVxdWFsKENvbmRpdGlvbkV4cHJlc3Npb24uZ2V0QXR0cmlidXRlVHlwZVN0cmluZyhOdW1iZXIpLCAnTicpO1xuXG4gICAgICBhc3NlcnQudGhyb3dzKCgpID0+XG4gICAgICAgIENvbmRpdGlvbkV4cHJlc3Npb24uZ2V0QXR0cmlidXRlVHlwZVN0cmluZygyIGFzIG5ldmVyKSxcbiAgICAgICk7XG4gICAgfSk7XG5cbiAgICBpdCgnTnVtYmVyIFNldCcsICgpID0+IHtcbiAgICAgIGFzc2VydC5lcXVhbChDb25kaXRpb25FeHByZXNzaW9uLmdldEF0dHJpYnV0ZVR5cGVTdHJpbmcoJ05TJyksICdOUycpO1xuICAgICAgYXNzZXJ0LmVxdWFsKENvbmRpdGlvbkV4cHJlc3Npb24uZ2V0QXR0cmlidXRlVHlwZVN0cmluZyhbTnVtYmVyXSksICdOUycpO1xuICAgIH0pO1xuXG4gICAgaXQoJ0JpbmFyeScsICgpID0+IHtcbiAgICAgIGFzc2VydC5lcXVhbChDb25kaXRpb25FeHByZXNzaW9uLmdldEF0dHJpYnV0ZVR5cGVTdHJpbmcoJ0InKSwgJ0InKTtcbiAgICAgIGFzc2VydC5lcXVhbChDb25kaXRpb25FeHByZXNzaW9uLmdldEF0dHJpYnV0ZVR5cGVTdHJpbmcoQnVmZmVyKSwgJ0InKTtcbiAgICAgIGFzc2VydC5lcXVhbChcbiAgICAgICAgQ29uZGl0aW9uRXhwcmVzc2lvbi5nZXRBdHRyaWJ1dGVUeXBlU3RyaW5nKEFycmF5QnVmZmVyKSxcbiAgICAgICAgJ0InLFxuICAgICAgKTtcbiAgICB9KTtcblxuICAgIGl0KCdCdWZmZXIgU2V0JywgKCkgPT4ge1xuICAgICAgYXNzZXJ0LmVxdWFsKENvbmRpdGlvbkV4cHJlc3Npb24uZ2V0QXR0cmlidXRlVHlwZVN0cmluZygnQlMnKSwgJ0JTJyk7XG4gICAgICBhc3NlcnQuZXF1YWwoQ29uZGl0aW9uRXhwcmVzc2lvbi5nZXRBdHRyaWJ1dGVUeXBlU3RyaW5nKFtCdWZmZXJdKSwgJ0JTJyk7XG4gICAgICBhc3NlcnQuZXF1YWwoXG4gICAgICAgIENvbmRpdGlvbkV4cHJlc3Npb24uZ2V0QXR0cmlidXRlVHlwZVN0cmluZyhbQXJyYXlCdWZmZXJdKSxcbiAgICAgICAgJ0JTJyxcbiAgICAgICk7XG4gICAgfSk7XG5cbiAgICBpdCgnQm9vbGVhbicsICgpID0+IHtcbiAgICAgIGFzc2VydC5lcXVhbChDb25kaXRpb25FeHByZXNzaW9uLmdldEF0dHJpYnV0ZVR5cGVTdHJpbmcoJ0JPT0wnKSwgJ0JPT0wnKTtcbiAgICAgIGFzc2VydC5lcXVhbChDb25kaXRpb25FeHByZXNzaW9uLmdldEF0dHJpYnV0ZVR5cGVTdHJpbmcoQm9vbGVhbiksICdCT09MJyk7XG5cbiAgICAgIGFzc2VydC50aHJvd3MoKCkgPT5cbiAgICAgICAgQ29uZGl0aW9uRXhwcmVzc2lvbi5nZXRBdHRyaWJ1dGVUeXBlU3RyaW5nKHRydWUgYXMgbmV2ZXIpLFxuICAgICAgKTtcbiAgICB9KTtcblxuICAgIGl0KCdOdWxsJywgKCkgPT4ge1xuICAgICAgYXNzZXJ0LmVxdWFsKENvbmRpdGlvbkV4cHJlc3Npb24uZ2V0QXR0cmlidXRlVHlwZVN0cmluZygnTlVMTCcpLCAnTlVMTCcpO1xuICAgICAgYXNzZXJ0LmVxdWFsKENvbmRpdGlvbkV4cHJlc3Npb24uZ2V0QXR0cmlidXRlVHlwZVN0cmluZyhudWxsKSwgJ05VTEwnKTtcblxuICAgICAgYXNzZXJ0LnRocm93cygoKSA9PlxuICAgICAgICBDb25kaXRpb25FeHByZXNzaW9uLmdldEF0dHJpYnV0ZVR5cGVTdHJpbmcodW5kZWZpbmVkIGFzIG5ldmVyKSxcbiAgICAgICk7XG4gICAgfSk7XG5cbiAgICBpdCgnTGlzdCcsICgpID0+IHtcbiAgICAgIGFzc2VydC5lcXVhbChDb25kaXRpb25FeHByZXNzaW9uLmdldEF0dHJpYnV0ZVR5cGVTdHJpbmcoJ0wnKSwgJ0wnKTtcbiAgICAgIGFzc2VydC5lcXVhbChDb25kaXRpb25FeHByZXNzaW9uLmdldEF0dHJpYnV0ZVR5cGVTdHJpbmcoQXJyYXkpLCAnTCcpO1xuXG4gICAgICBhc3NlcnQudGhyb3dzKCgpID0+XG4gICAgICAgIENvbmRpdGlvbkV4cHJlc3Npb24uZ2V0QXR0cmlidXRlVHlwZVN0cmluZyhbXSBhcyBuZXZlciksXG4gICAgICApO1xuICAgIH0pO1xuXG4gICAgaXQoJ01hcCcsICgpID0+IHtcbiAgICAgIGFzc2VydC5lcXVhbChDb25kaXRpb25FeHByZXNzaW9uLmdldEF0dHJpYnV0ZVR5cGVTdHJpbmcoJ00nKSwgJ00nKTtcbiAgICAgIGFzc2VydC5lcXVhbChDb25kaXRpb25FeHByZXNzaW9uLmdldEF0dHJpYnV0ZVR5cGVTdHJpbmcoT2JqZWN0KSwgJ00nKTtcblxuICAgICAgYXNzZXJ0LnRocm93cygoKSA9PlxuICAgICAgICBDb25kaXRpb25FeHByZXNzaW9uLmdldEF0dHJpYnV0ZVR5cGVTdHJpbmcoe30gYXMgbmV2ZXIpLFxuICAgICAgKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJ2luaXQnLCAoKSA9PiB7XG4gICAgaXQoJ2Zyb20gb3RoZXIgZXhwcmVzc2lvbicsICgpID0+IHtcbiAgICAgIGNvbnN0IG5hbWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVOYW1lcygpO1xuICAgICAgY29uc3QgdmFsdWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVWYWx1ZXMoKTtcbiAgICAgIGNvbnN0IGV4cCA9IG5ldyBDb25kaXRpb25FeHByZXNzaW9uKG5hbWVzLCB2YWx1ZXMsICcjZm9vID0gOmZvbycpO1xuXG4gICAgICBjb25zdCBleHAyID0gbmV3IENvbmRpdGlvbkV4cHJlc3Npb24obmFtZXMsIHZhbHVlcywgZXhwKTtcblxuICAgICAgYXNzZXJ0LmVxdWFsKGV4cC5zZXJpYWxpemUoKSwgZXhwMi5zZXJpYWxpemUoKSk7XG4gICAgfSk7XG4gIH0pO1xuXG4gIGRlc2NyaWJlKCdpbml0IC0+IHNlcmlhbGl6ZSgpJywgKCkgPT4ge1xuICAgIGZ1bmN0aW9uIGFzc2V0U2VyaWFsaXplKFxuICAgICAgdGVzdDogc3RyaW5nLFxuICAgICAgZXhwZWN0ZWQ6IHN0cmluZyA9IHRlc3QudHJpbSgpLnJlcGxhY2UoL1xccysvZywgJyAnKSxcbiAgICApOiB2b2lkIHtcbiAgICAgIGl0KHRlc3QsICgpID0+IHtcbiAgICAgICAgY29uc3QgbmFtZXMgPSBuZXcgRXhwcmVzc2lvbkF0dHJpYnV0ZU5hbWVzKCk7XG4gICAgICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlVmFsdWVzKCk7XG4gICAgICAgIGNvbnN0IGV4cCA9IG5ldyBDb25kaXRpb25FeHByZXNzaW9uKG5hbWVzLCB2YWx1ZXMsIHRlc3QpO1xuXG4gICAgICAgIGFzc2VydC5lcXVhbChleHAuc2VyaWFsaXplKCksIGV4cGVjdGVkKTtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGFzc2V0U2VyaWFsaXplKCcjZm9vID0gOmZvbycpO1xuICAgIGFzc2V0U2VyaWFsaXplKCc6Zm9vID0gI2ZvbycpO1xuICAgIGFzc2V0U2VyaWFsaXplKCcjZm9vID0gOmZvbyBBTkQgI2JhciA8PiA6YmFyJyk7XG4gICAgYXNzZXRTZXJpYWxpemUoJyNmb28gPSA6Zm9vIE9SICNiYXIgPiA6YmFyJyk7XG4gICAgYXNzZXRTZXJpYWxpemUoJyNmb28gPCA6Zm9vIEFORCAjYmFyID49IDpiYXIgT1IgI3pvbyA9IDp6b28nKTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJy5uYW1lJywgKCkgPT4ge1xuICAgIGl0KCdiYXNpYyB1c2UgY2FzZScsICgpID0+IHtcbiAgICAgIGNvbnN0IG5hbWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVOYW1lcygpO1xuICAgICAgY29uc3QgdmFsdWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVWYWx1ZXMoKTtcbiAgICAgIGNvbnN0IGV4cCA9IG5ldyBDb25kaXRpb25FeHByZXNzaW9uPHsgZm9vOiBudW1iZXIgfT4obmFtZXMsIHZhbHVlcyk7XG5cbiAgICAgIGFzc2VydC5lcXVhbChleHAubmFtZSgnZm9vJyksICcjZm9vJyk7XG4gICAgICBhc3NlcnQuZGVlcEVxdWFsKG5hbWVzLnNlcmlhbGl6ZSgpLCB7ICcjZm9vJzogJ2ZvbycgfSk7XG4gICAgICBhc3NlcnQuZGVlcEVxdWFsKHZhbHVlcy5zZXJpYWxpemUoKSwgdW5kZWZpbmVkKTtcbiAgICB9KTtcblxuICAgIGl0KCdpbm5lciBwYXRoJywgKCkgPT4ge1xuICAgICAgY29uc3QgbmFtZXMgPSBuZXcgRXhwcmVzc2lvbkF0dHJpYnV0ZU5hbWVzKCk7XG4gICAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRXhwcmVzc2lvbkF0dHJpYnV0ZVZhbHVlcygpO1xuICAgICAgY29uc3QgZXhwID0gbmV3IENvbmRpdGlvbkV4cHJlc3Npb248eyBmb286IHsgYmFyOiBudW1iZXIgfSB9PihcbiAgICAgICAgbmFtZXMsXG4gICAgICAgIHZhbHVlcyxcbiAgICAgICk7XG5cbiAgICAgIGFzc2VydC5lcXVhbChleHAubmFtZSgnZm9vJywgJ2JhcicpLCAnI2Zvby4jYmFyJyk7XG4gICAgICBhc3NlcnQuZGVlcEVxdWFsKG5hbWVzLnNlcmlhbGl6ZSgpLCB7ICcjZm9vJzogJ2ZvbycsICcjYmFyJzogJ2JhcicgfSk7XG4gICAgICBhc3NlcnQuZGVlcEVxdWFsKHZhbHVlcy5zZXJpYWxpemUoKSwgdW5kZWZpbmVkKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJy52YWx1ZScsICgpID0+IHtcbiAgICBpdCgnYmFzaWMgdXNlIGNhc2UnLCAoKSA9PiB7XG4gICAgICBjb25zdCBuYW1lcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlTmFtZXMoKTtcbiAgICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlVmFsdWVzKCk7XG4gICAgICBjb25zdCBleHAgPSBuZXcgQ29uZGl0aW9uRXhwcmVzc2lvbjx7IGZvbzogbnVtYmVyIH0+KG5hbWVzLCB2YWx1ZXMpO1xuXG4gICAgICBhc3NlcnQuZXF1YWwoZXhwLnZhbHVlKCdmb28nLCAxKSwgJzpmb28nKTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwobmFtZXMuc2VyaWFsaXplKCksIHVuZGVmaW5lZCk7XG4gICAgICBhc3NlcnQuZGVlcEVxdWFsKHZhbHVlcy5zZXJpYWxpemUoKSwgeyAnOmZvbyc6IDEgfSk7XG4gICAgfSk7XG5cbiAgICBpdCgnZHVwbGljYXRlIG5hbWUnLCAoKSA9PiB7XG4gICAgICBjb25zdCBuYW1lcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlTmFtZXMoKTtcbiAgICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlVmFsdWVzKCk7XG4gICAgICBjb25zdCBleHAgPSBuZXcgQ29uZGl0aW9uRXhwcmVzc2lvbjx7IGZvbzogeyBiYXI6IG51bWJlciB9IH0+KFxuICAgICAgICBuYW1lcyxcbiAgICAgICAgdmFsdWVzLFxuICAgICAgKTtcblxuICAgICAgYXNzZXJ0LmVxdWFsKGV4cC52YWx1ZSgnZm9vJywgMSksICc6Zm9vJyk7XG4gICAgICBhc3NlcnQuZXF1YWwoZXhwLnZhbHVlKCdmb28nLCAyKSwgJzpmb28yJyk7XG4gICAgICBhc3NlcnQuZGVlcEVxdWFsKG5hbWVzLnNlcmlhbGl6ZSgpLCB1bmRlZmluZWQpO1xuICAgICAgYXNzZXJ0LmRlZXBFcXVhbCh2YWx1ZXMuc2VyaWFsaXplKCksIHsgJzpmb28nOiAxLCAnOmZvbzInOiAyIH0pO1xuICAgIH0pO1xuICB9KTtcblxuICBkZXNjcmliZSgnLmVxJywgKCkgPT4ge1xuICAgIGl0KCdiYXNpYyB1c2UgY2FzZScsICgpID0+IHtcbiAgICAgIGNvbnN0IG5hbWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVOYW1lcygpO1xuICAgICAgY29uc3QgdmFsdWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVWYWx1ZXMoKTtcbiAgICAgIGNvbnN0IGV4cCA9IG5ldyBDb25kaXRpb25FeHByZXNzaW9uPHsgZm9vOiBudW1iZXIgfT4obmFtZXMsIHZhbHVlcyk7XG5cbiAgICAgIGV4cC5lcSgnZm9vJywgMSk7XG5cbiAgICAgIGFzc2VydC5lcXVhbChleHAuc2VyaWFsaXplKCksICcjZm9vID0gOmZvbycpO1xuICAgICAgYXNzZXJ0LmRlZXBFcXVhbChuYW1lcy5zZXJpYWxpemUoKSwgeyAnI2Zvbyc6ICdmb28nIH0pO1xuICAgICAgYXNzZXJ0LmRlZXBFcXVhbCh2YWx1ZXMuc2VyaWFsaXplKCksIHsgJzpmb28nOiAxIH0pO1xuICAgIH0pO1xuICB9KTtcblxuICBkZXNjcmliZSgnLm5xJywgKCkgPT4ge1xuICAgIGl0KCdiYXNpYyB1c2UgY2FzZScsICgpID0+IHtcbiAgICAgIGNvbnN0IG5hbWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVOYW1lcygpO1xuICAgICAgY29uc3QgdmFsdWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVWYWx1ZXMoKTtcbiAgICAgIGNvbnN0IGV4cCA9IG5ldyBDb25kaXRpb25FeHByZXNzaW9uPHsgZm9vOiBudW1iZXIgfT4obmFtZXMsIHZhbHVlcyk7XG5cbiAgICAgIGV4cC5ucSgnZm9vJywgMSk7XG5cbiAgICAgIGFzc2VydC5lcXVhbChleHAuc2VyaWFsaXplKCksICcjZm9vIDw+IDpmb28nKTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwobmFtZXMuc2VyaWFsaXplKCksIHsgJyNmb28nOiAnZm9vJyB9KTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwodmFsdWVzLnNlcmlhbGl6ZSgpLCB7ICc6Zm9vJzogMSB9KTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJy5sdCcsICgpID0+IHtcbiAgICBpdCgnYmFzaWMgdXNlIGNhc2UnLCAoKSA9PiB7XG4gICAgICBjb25zdCBuYW1lcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlTmFtZXMoKTtcbiAgICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlVmFsdWVzKCk7XG4gICAgICBjb25zdCBleHAgPSBuZXcgQ29uZGl0aW9uRXhwcmVzc2lvbjx7IGZvbzogbnVtYmVyIH0+KG5hbWVzLCB2YWx1ZXMpO1xuXG4gICAgICBleHAubHQoJ2ZvbycsIDEpO1xuXG4gICAgICBhc3NlcnQuZXF1YWwoZXhwLnNlcmlhbGl6ZSgpLCAnI2ZvbyA8IDpmb28nKTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwobmFtZXMuc2VyaWFsaXplKCksIHsgJyNmb28nOiAnZm9vJyB9KTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwodmFsdWVzLnNlcmlhbGl6ZSgpLCB7ICc6Zm9vJzogMSB9KTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJy5sdGUnLCAoKSA9PiB7XG4gICAgaXQoJ2Jhc2ljIHVzZSBjYXNlJywgKCkgPT4ge1xuICAgICAgY29uc3QgbmFtZXMgPSBuZXcgRXhwcmVzc2lvbkF0dHJpYnV0ZU5hbWVzKCk7XG4gICAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRXhwcmVzc2lvbkF0dHJpYnV0ZVZhbHVlcygpO1xuICAgICAgY29uc3QgZXhwID0gbmV3IENvbmRpdGlvbkV4cHJlc3Npb248eyBmb286IG51bWJlciB9PihuYW1lcywgdmFsdWVzKTtcblxuICAgICAgZXhwLmx0ZSgnZm9vJywgMSk7XG5cbiAgICAgIGFzc2VydC5lcXVhbChleHAuc2VyaWFsaXplKCksICcjZm9vIDw9IDpmb28nKTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwobmFtZXMuc2VyaWFsaXplKCksIHsgJyNmb28nOiAnZm9vJyB9KTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwodmFsdWVzLnNlcmlhbGl6ZSgpLCB7ICc6Zm9vJzogMSB9KTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJy5ndCcsICgpID0+IHtcbiAgICBpdCgnYmFzaWMgdXNlIGNhc2UnLCAoKSA9PiB7XG4gICAgICBjb25zdCBuYW1lcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlTmFtZXMoKTtcbiAgICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlVmFsdWVzKCk7XG4gICAgICBjb25zdCBleHAgPSBuZXcgQ29uZGl0aW9uRXhwcmVzc2lvbjx7IGZvbzogbnVtYmVyIH0+KG5hbWVzLCB2YWx1ZXMpO1xuXG4gICAgICBleHAuZ3QoJ2ZvbycsIDEpO1xuXG4gICAgICBhc3NlcnQuZXF1YWwoZXhwLnNlcmlhbGl6ZSgpLCAnI2ZvbyA+IDpmb28nKTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwobmFtZXMuc2VyaWFsaXplKCksIHsgJyNmb28nOiAnZm9vJyB9KTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwodmFsdWVzLnNlcmlhbGl6ZSgpLCB7ICc6Zm9vJzogMSB9KTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJy5ndGUnLCAoKSA9PiB7XG4gICAgaXQoJ2Jhc2ljIHVzZSBjYXNlJywgKCkgPT4ge1xuICAgICAgY29uc3QgbmFtZXMgPSBuZXcgRXhwcmVzc2lvbkF0dHJpYnV0ZU5hbWVzKCk7XG4gICAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRXhwcmVzc2lvbkF0dHJpYnV0ZVZhbHVlcygpO1xuICAgICAgY29uc3QgZXhwID0gbmV3IENvbmRpdGlvbkV4cHJlc3Npb248eyBmb286IG51bWJlciB9PihuYW1lcywgdmFsdWVzKTtcblxuICAgICAgZXhwLmd0ZSgnZm9vJywgMSk7XG5cbiAgICAgIGFzc2VydC5lcXVhbChleHAuc2VyaWFsaXplKCksICcjZm9vID49IDpmb28nKTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwobmFtZXMuc2VyaWFsaXplKCksIHsgJyNmb28nOiAnZm9vJyB9KTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwodmFsdWVzLnNlcmlhbGl6ZSgpLCB7ICc6Zm9vJzogMSB9KTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJ2NvbXBhcmF0b3InLCAoKSA9PiB7XG4gICAgaXQoJ2ZvbyA8PiAxJywgKCkgPT4ge1xuICAgICAgY29uc3QgbmFtZXMgPSBuZXcgRXhwcmVzc2lvbkF0dHJpYnV0ZU5hbWVzKCk7XG4gICAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRXhwcmVzc2lvbkF0dHJpYnV0ZVZhbHVlcygpO1xuICAgICAgY29uc3QgZXhwID0gbmV3IENvbmRpdGlvbkV4cHJlc3Npb248eyBmb286IG51bWJlciB9PihuYW1lcywgdmFsdWVzKTtcblxuICAgICAgZXhwLmNvbXBhcmF0b3IoJzw+JywgJ2ZvbycsIDEpO1xuXG4gICAgICBhc3NlcnQuZXF1YWwoZXhwLnNlcmlhbGl6ZSgpLCAnI2ZvbyA8PiA6Zm9vJyk7XG4gICAgICBhc3NlcnQuZGVlcEVxdWFsKG5hbWVzLnNlcmlhbGl6ZSgpLCB7ICcjZm9vJzogJ2ZvbycgfSk7XG4gICAgICBhc3NlcnQuZGVlcEVxdWFsKHZhbHVlcy5zZXJpYWxpemUoKSwgeyAnOmZvbyc6IDEgfSk7XG4gICAgfSk7XG5cbiAgICBpdCgnZm9vLmJhciA9IDEnLCAoKSA9PiB7XG4gICAgICBjb25zdCBuYW1lcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlTmFtZXMoKTtcbiAgICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlVmFsdWVzKCk7XG4gICAgICBjb25zdCBleHAgPSBuZXcgQ29uZGl0aW9uRXhwcmVzc2lvbjx7IGZvbzogeyBiYXI6IG51bWJlciB9IH0+KFxuICAgICAgICBuYW1lcyxcbiAgICAgICAgdmFsdWVzLFxuICAgICAgKTtcblxuICAgICAgZXhwLmNvbXBhcmF0b3IoJzw+JywgWydmb28nLCAnYmFyJ10sIDEpO1xuXG4gICAgICBhc3NlcnQuZXF1YWwoZXhwLnNlcmlhbGl6ZSgpLCAnI2Zvby4jYmFyIDw+IDpiYXInKTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwobmFtZXMuc2VyaWFsaXplKCksIHsgJyNmb28nOiAnZm9vJywgJyNiYXInOiAnYmFyJyB9KTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwodmFsdWVzLnNlcmlhbGl6ZSgpLCB7ICc6YmFyJzogMSB9KTtcbiAgICB9KTtcblxuICAgIGl0KCdmb28gPD4gZnVuY3Rpb24nLCAoKSA9PiB7XG4gICAgICBjb25zdCBuYW1lcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlTmFtZXMoKTtcbiAgICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlVmFsdWVzKCk7XG4gICAgICBjb25zdCBleHAgPSBuZXcgQ29uZGl0aW9uRXhwcmVzc2lvbjx7IGZvbzogbnVtYmVyIH0+KG5hbWVzLCB2YWx1ZXMpO1xuXG4gICAgICBleHAuY29tcGFyYXRvcignPD4nLCBbJ2ZvbyddLCAocGF0aCwgY24pID0+IGNuLmZuKCdzaXplJywgcGF0aCkpO1xuXG4gICAgICBhc3NlcnQuZXF1YWwoZXhwLnNlcmlhbGl6ZSgpLCAnI2ZvbyA8PiBzaXplKCNmb28pJyk7XG4gICAgICBhc3NlcnQuZGVlcEVxdWFsKG5hbWVzLnNlcmlhbGl6ZSgpLCB7ICcjZm9vJzogJ2ZvbycgfSk7XG4gICAgICBhc3NlcnQuZGVlcEVxdWFsKHZhbHVlcy5zZXJpYWxpemUoKSwgdW5kZWZpbmVkKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJy5iZXR3ZWVuJywgKCkgPT4ge1xuICAgIGl0KCdiYXNpYyB1c2UgY2FzZScsICgpID0+IHtcbiAgICAgIGNvbnN0IG5hbWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVOYW1lcygpO1xuICAgICAgY29uc3QgdmFsdWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVWYWx1ZXMoKTtcbiAgICAgIGNvbnN0IGV4cCA9IG5ldyBDb25kaXRpb25FeHByZXNzaW9uPHsgZm9vOiBudW1iZXIgfT4obmFtZXMsIHZhbHVlcyk7XG5cbiAgICAgIGV4cC5iZXR3ZWVuKCdmb28nLCAxLCAyKTtcblxuICAgICAgYXNzZXJ0LmVxdWFsKGV4cC5zZXJpYWxpemUoKSwgJyNmb28gQkVUV0VFTiA6Zm9vIEFORCA6Zm9vMicpO1xuICAgICAgYXNzZXJ0LmRlZXBFcXVhbChuYW1lcy5zZXJpYWxpemUoKSwgeyAnI2Zvbyc6ICdmb28nIH0pO1xuICAgICAgYXNzZXJ0LmRlZXBFcXVhbCh2YWx1ZXMuc2VyaWFsaXplKCksIHsgJzpmb28nOiAxLCAnOmZvbzInOiAyIH0pO1xuICAgIH0pO1xuXG4gICAgaXQoJ2lubmVyIGtleScsICgpID0+IHtcbiAgICAgIGNvbnN0IG5hbWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVOYW1lcygpO1xuICAgICAgY29uc3QgdmFsdWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVWYWx1ZXMoKTtcbiAgICAgIGNvbnN0IGV4cCA9IG5ldyBDb25kaXRpb25FeHByZXNzaW9uPHsgZm9vOiB7IGJhcjogbnVtYmVyIH0gfT4oXG4gICAgICAgIG5hbWVzLFxuICAgICAgICB2YWx1ZXMsXG4gICAgICApO1xuXG4gICAgICBleHAuYmV0d2VlbihbJ2ZvbycsICdiYXInXSwgMSwgMik7XG5cbiAgICAgIGFzc2VydC5lcXVhbChleHAuc2VyaWFsaXplKCksICcjZm9vLiNiYXIgQkVUV0VFTiA6YmFyIEFORCA6YmFyMicpO1xuICAgICAgYXNzZXJ0LmRlZXBFcXVhbChuYW1lcy5zZXJpYWxpemUoKSwgeyAnI2Zvbyc6ICdmb28nLCAnI2Jhcic6ICdiYXInIH0pO1xuICAgICAgYXNzZXJ0LmRlZXBFcXVhbCh2YWx1ZXMuc2VyaWFsaXplKCksIHsgJzpiYXInOiAxLCAnOmJhcjInOiAyIH0pO1xuICAgIH0pO1xuICB9KTtcblxuICBkZXNjcmliZSgnLmF0dHJpYnV0ZUV4aXN0cycsICgpID0+IHtcbiAgICBpdCgnYmFzaWMgdXNlIGNhc2UnLCAoKSA9PiB7XG4gICAgICBjb25zdCBuYW1lcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlTmFtZXMoKTtcbiAgICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlVmFsdWVzKCk7XG4gICAgICBjb25zdCBleHAgPSBuZXcgQ29uZGl0aW9uRXhwcmVzc2lvbjx7IGZvbzogbnVtYmVyIH0+KG5hbWVzLCB2YWx1ZXMpO1xuXG4gICAgICBleHAuYXR0cmlidXRlRXhpc3RzKCdmb28nKTtcblxuICAgICAgYXNzZXJ0LmVxdWFsKGV4cC5zZXJpYWxpemUoKSwgJ2F0dHJpYnV0ZV9leGlzdHMoI2ZvbyknKTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwobmFtZXMuc2VyaWFsaXplKCksIHsgJyNmb28nOiAnZm9vJyB9KTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwodmFsdWVzLnNlcmlhbGl6ZSgpLCB1bmRlZmluZWQpO1xuICAgIH0pO1xuXG4gICAgaXQoJ2lubmVyIHBhdGgnLCAoKSA9PiB7XG4gICAgICBjb25zdCBuYW1lcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlTmFtZXMoKTtcbiAgICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlVmFsdWVzKCk7XG4gICAgICBjb25zdCBleHAgPSBuZXcgQ29uZGl0aW9uRXhwcmVzc2lvbjx7IGZvbzogeyBiYXI6IG51bWJlciB9IH0+KFxuICAgICAgICBuYW1lcyxcbiAgICAgICAgdmFsdWVzLFxuICAgICAgKTtcblxuICAgICAgZXhwLmF0dHJpYnV0ZUV4aXN0cyhbJ2ZvbycsICdiYXInXSk7XG5cbiAgICAgIGFzc2VydC5lcXVhbChleHAuc2VyaWFsaXplKCksICdhdHRyaWJ1dGVfZXhpc3RzKCNmb28uI2JhciknKTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwobmFtZXMuc2VyaWFsaXplKCksIHsgJyNmb28nOiAnZm9vJywgJyNiYXInOiAnYmFyJyB9KTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwodmFsdWVzLnNlcmlhbGl6ZSgpLCB1bmRlZmluZWQpO1xuICAgIH0pO1xuICB9KTtcblxuICBkZXNjcmliZSgnLmF0dHJpYnV0ZU5vdEV4aXN0cycsICgpID0+IHtcbiAgICBpdCgnYmFzaWMgdXNlIGNhc2UnLCAoKSA9PiB7XG4gICAgICBjb25zdCBuYW1lcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlTmFtZXMoKTtcbiAgICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlVmFsdWVzKCk7XG4gICAgICBjb25zdCBleHAgPSBuZXcgQ29uZGl0aW9uRXhwcmVzc2lvbjx7IGZvbzogbnVtYmVyIH0+KG5hbWVzLCB2YWx1ZXMpO1xuXG4gICAgICBleHAuYXR0cmlidXRlTm90RXhpc3RzKCdmb28nKTtcblxuICAgICAgYXNzZXJ0LmVxdWFsKGV4cC5zZXJpYWxpemUoKSwgJ2F0dHJpYnV0ZV9ub3RfZXhpc3RzKCNmb28pJyk7XG4gICAgICBhc3NlcnQuZGVlcEVxdWFsKG5hbWVzLnNlcmlhbGl6ZSgpLCB7ICcjZm9vJzogJ2ZvbycgfSk7XG4gICAgICBhc3NlcnQuZGVlcEVxdWFsKHZhbHVlcy5zZXJpYWxpemUoKSwgdW5kZWZpbmVkKTtcbiAgICB9KTtcblxuICAgIGl0KCdpbm5lciBwYXRoJywgKCkgPT4ge1xuICAgICAgY29uc3QgbmFtZXMgPSBuZXcgRXhwcmVzc2lvbkF0dHJpYnV0ZU5hbWVzKCk7XG4gICAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRXhwcmVzc2lvbkF0dHJpYnV0ZVZhbHVlcygpO1xuICAgICAgY29uc3QgZXhwID0gbmV3IENvbmRpdGlvbkV4cHJlc3Npb248eyBmb286IHsgYmFyOiBudW1iZXIgfSB9PihcbiAgICAgICAgbmFtZXMsXG4gICAgICAgIHZhbHVlcyxcbiAgICAgICk7XG5cbiAgICAgIGV4cC5hdHRyaWJ1dGVOb3RFeGlzdHMoWydmb28nLCAnYmFyJ10pO1xuXG4gICAgICBhc3NlcnQuZXF1YWwoZXhwLnNlcmlhbGl6ZSgpLCAnYXR0cmlidXRlX25vdF9leGlzdHMoI2Zvby4jYmFyKScpO1xuICAgICAgYXNzZXJ0LmRlZXBFcXVhbChuYW1lcy5zZXJpYWxpemUoKSwgeyAnI2Zvbyc6ICdmb28nLCAnI2Jhcic6ICdiYXInIH0pO1xuICAgICAgYXNzZXJ0LmRlZXBFcXVhbCh2YWx1ZXMuc2VyaWFsaXplKCksIHVuZGVmaW5lZCk7XG4gICAgfSk7XG4gIH0pO1xuXG4gIGRlc2NyaWJlKCcuYXR0cmlidXRlVHlwZScsICgpID0+IHtcbiAgICBpdCgnYmFzaWMgdXNlIGNhc2UnLCAoKSA9PiB7XG4gICAgICBjb25zdCBuYW1lcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlTmFtZXMoKTtcbiAgICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlVmFsdWVzKCk7XG4gICAgICBjb25zdCBleHAgPSBuZXcgQ29uZGl0aW9uRXhwcmVzc2lvbjx7IGZvbzogbnVtYmVyIH0+KG5hbWVzLCB2YWx1ZXMpO1xuXG4gICAgICBleHAuYXR0cmlidXRlVHlwZSgnZm9vJywgTnVtYmVyKTtcblxuICAgICAgYXNzZXJ0LmVxdWFsKGV4cC5zZXJpYWxpemUoKSwgJ2F0dHJpYnV0ZV90eXBlKCNmb28sIDpmb28pJyk7XG4gICAgICBhc3NlcnQuZGVlcEVxdWFsKG5hbWVzLnNlcmlhbGl6ZSgpLCB7ICcjZm9vJzogJ2ZvbycgfSk7XG4gICAgICBhc3NlcnQuZGVlcEVxdWFsKHZhbHVlcy5zZXJpYWxpemUoKSwgeyAnOmZvbyc6ICdOJyB9KTtcbiAgICB9KTtcblxuICAgIGl0KCdpbm5lciBwYXRoJywgKCkgPT4ge1xuICAgICAgY29uc3QgbmFtZXMgPSBuZXcgRXhwcmVzc2lvbkF0dHJpYnV0ZU5hbWVzKCk7XG4gICAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRXhwcmVzc2lvbkF0dHJpYnV0ZVZhbHVlcygpO1xuICAgICAgY29uc3QgZXhwID0gbmV3IENvbmRpdGlvbkV4cHJlc3Npb248eyBmb286IHsgYmFyOiBudW1iZXIgfSB9PihcbiAgICAgICAgbmFtZXMsXG4gICAgICAgIHZhbHVlcyxcbiAgICAgICk7XG5cbiAgICAgIGV4cC5hdHRyaWJ1dGVUeXBlKFsnZm9vJywgJ2JhciddLCBbU3RyaW5nXSk7XG5cbiAgICAgIGFzc2VydC5lcXVhbChleHAuc2VyaWFsaXplKCksICdhdHRyaWJ1dGVfdHlwZSgjZm9vLiNiYXIsIDpiYXIpJyk7XG4gICAgICBhc3NlcnQuZGVlcEVxdWFsKG5hbWVzLnNlcmlhbGl6ZSgpLCB7ICcjZm9vJzogJ2ZvbycsICcjYmFyJzogJ2JhcicgfSk7XG4gICAgICBhc3NlcnQuZGVlcEVxdWFsKHZhbHVlcy5zZXJpYWxpemUoKSwgeyAnOmJhcic6ICdTUycgfSk7XG4gICAgfSk7XG4gIH0pO1xuXG4gIGRlc2NyaWJlKCcuYmVnaW5zV2l0aCcsICgpID0+IHtcbiAgICBpdCgnYmFzaWMgdXNlIGNhc2UnLCAoKSA9PiB7XG4gICAgICBjb25zdCBuYW1lcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlTmFtZXMoKTtcbiAgICAgIGNvbnN0IHZhbHVlcyA9IG5ldyBFeHByZXNzaW9uQXR0cmlidXRlVmFsdWVzKCk7XG4gICAgICBjb25zdCBleHAgPSBuZXcgQ29uZGl0aW9uRXhwcmVzc2lvbjx7IGZvbzogbnVtYmVyIH0+KG5hbWVzLCB2YWx1ZXMpO1xuXG4gICAgICBleHAuYmVnaW5zV2l0aCgnZm9vJywgJ3N0cicpO1xuXG4gICAgICBhc3NlcnQuZXF1YWwoZXhwLnNlcmlhbGl6ZSgpLCAnYmVnaW5zX3dpdGgoI2ZvbywgOmZvbyknKTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwobmFtZXMuc2VyaWFsaXplKCksIHsgJyNmb28nOiAnZm9vJyB9KTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwodmFsdWVzLnNlcmlhbGl6ZSgpLCB7ICc6Zm9vJzogJ3N0cicgfSk7XG4gICAgfSk7XG5cbiAgICBpdCgnaW5uZXIgcGF0aCcsICgpID0+IHtcbiAgICAgIGNvbnN0IG5hbWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVOYW1lcygpO1xuICAgICAgY29uc3QgdmFsdWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVWYWx1ZXMoKTtcbiAgICAgIGNvbnN0IGV4cCA9IG5ldyBDb25kaXRpb25FeHByZXNzaW9uPHsgZm9vOiB7IGJhcjogbnVtYmVyIH0gfT4oXG4gICAgICAgIG5hbWVzLFxuICAgICAgICB2YWx1ZXMsXG4gICAgICApO1xuXG4gICAgICBleHAuYmVnaW5zV2l0aChbJ2ZvbycsICdiYXInXSwgJ3N0cjInKTtcblxuICAgICAgYXNzZXJ0LmVxdWFsKGV4cC5zZXJpYWxpemUoKSwgJ2JlZ2luc193aXRoKCNmb28uI2JhciwgOmJhciknKTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwobmFtZXMuc2VyaWFsaXplKCksIHsgJyNmb28nOiAnZm9vJywgJyNiYXInOiAnYmFyJyB9KTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwodmFsdWVzLnNlcmlhbGl6ZSgpLCB7ICc6YmFyJzogJ3N0cjInIH0pO1xuICAgIH0pO1xuICB9KTtcblxuICBkZXNjcmliZSgnLmNvbnRhaW5zJywgKCkgPT4ge1xuICAgIGl0KCdiYXNpYyB1c2UgY2FzZScsICgpID0+IHtcbiAgICAgIGNvbnN0IG5hbWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVOYW1lcygpO1xuICAgICAgY29uc3QgdmFsdWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVWYWx1ZXMoKTtcbiAgICAgIGNvbnN0IGV4cCA9IG5ldyBDb25kaXRpb25FeHByZXNzaW9uPHsgZm9vOiBzdHJpbmcgfT4obmFtZXMsIHZhbHVlcyk7XG5cbiAgICAgIGV4cC5jb250YWlucygnZm9vJywgJ3N0cicpO1xuXG4gICAgICBhc3NlcnQuZXF1YWwoZXhwLnNlcmlhbGl6ZSgpLCAnY29udGFpbnMoI2ZvbywgOmZvbyknKTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwobmFtZXMuc2VyaWFsaXplKCksIHsgJyNmb28nOiAnZm9vJyB9KTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwodmFsdWVzLnNlcmlhbGl6ZSgpLCB7ICc6Zm9vJzogJ3N0cicgfSk7XG4gICAgfSk7XG5cbiAgICBpdCgnc3RyaW5nIHNldCB1c2UgY2FzZScsICgpID0+IHtcbiAgICAgIGNvbnN0IG5hbWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVOYW1lcygpO1xuICAgICAgY29uc3QgdmFsdWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVWYWx1ZXMoKTtcbiAgICAgIGNvbnN0IGV4cCA9IG5ldyBDb25kaXRpb25FeHByZXNzaW9uPHsgZm9vOiBTZXQ8c3RyaW5nPiB9PihuYW1lcywgdmFsdWVzKTtcblxuICAgICAgZXhwLmNvbnRhaW5zKCdmb28nLCAnc3RyJyk7XG5cbiAgICAgIGFzc2VydC5lcXVhbChleHAuc2VyaWFsaXplKCksICdjb250YWlucygjZm9vLCA6Zm9vKScpO1xuICAgICAgYXNzZXJ0LmRlZXBFcXVhbChuYW1lcy5zZXJpYWxpemUoKSwgeyAnI2Zvbyc6ICdmb28nIH0pO1xuICAgICAgYXNzZXJ0LmRlZXBFcXVhbCh2YWx1ZXMuc2VyaWFsaXplKCksIHsgJzpmb28nOiAnc3RyJyB9KTtcbiAgICB9KTtcblxuICAgIGl0KCdudW1iZXIgc2V0IHVzZSBjYXNlJywgKCkgPT4ge1xuICAgICAgY29uc3QgbmFtZXMgPSBuZXcgRXhwcmVzc2lvbkF0dHJpYnV0ZU5hbWVzKCk7XG4gICAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRXhwcmVzc2lvbkF0dHJpYnV0ZVZhbHVlcygpO1xuICAgICAgY29uc3QgZXhwID0gbmV3IENvbmRpdGlvbkV4cHJlc3Npb248eyBmb286IFNldDxudW1iZXI+IH0+KG5hbWVzLCB2YWx1ZXMpO1xuXG4gICAgICBleHAuY29udGFpbnMoJ2ZvbycsIDEyMyk7XG5cbiAgICAgIGFzc2VydC5lcXVhbChleHAuc2VyaWFsaXplKCksICdjb250YWlucygjZm9vLCA6Zm9vKScpO1xuICAgICAgYXNzZXJ0LmRlZXBFcXVhbChuYW1lcy5zZXJpYWxpemUoKSwgeyAnI2Zvbyc6ICdmb28nIH0pO1xuICAgICAgYXNzZXJ0LmRlZXBFcXVhbCh2YWx1ZXMuc2VyaWFsaXplKCksIHsgJzpmb28nOiAxMjMgfSk7XG4gICAgfSk7XG5cbiAgICBpdCgnbnVsbGFibGUgc3RyaW5nIHNldCB1c2UgY2FzZScsICgpID0+IHtcbiAgICAgIGNvbnN0IG5hbWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVOYW1lcygpO1xuICAgICAgY29uc3QgdmFsdWVzID0gbmV3IEV4cHJlc3Npb25BdHRyaWJ1dGVWYWx1ZXMoKTtcbiAgICAgIGNvbnN0IGV4cCA9IG5ldyBDb25kaXRpb25FeHByZXNzaW9uPHsgZm9vPzogU2V0PHN0cmluZz4gfCBudWxsIH0+KFxuICAgICAgICBuYW1lcyxcbiAgICAgICAgdmFsdWVzLFxuICAgICAgKTtcblxuICAgICAgZXhwLmNvbnRhaW5zKCdmb28nLCAnc3RyJyk7XG5cbiAgICAgIGFzc2VydC5lcXVhbChleHAuc2VyaWFsaXplKCksICdjb250YWlucygjZm9vLCA6Zm9vKScpO1xuICAgICAgYXNzZXJ0LmRlZXBFcXVhbChuYW1lcy5zZXJpYWxpemUoKSwgeyAnI2Zvbyc6ICdmb28nIH0pO1xuICAgICAgYXNzZXJ0LmRlZXBFcXVhbCh2YWx1ZXMuc2VyaWFsaXplKCksIHsgJzpmb28nOiAnc3RyJyB9KTtcbiAgICB9KTtcblxuICAgIGl0KCdudWxsYWJsZSBudW1iZXIgc2V0IHVzZSBjYXNlJywgKCkgPT4ge1xuICAgICAgY29uc3QgbmFtZXMgPSBuZXcgRXhwcmVzc2lvbkF0dHJpYnV0ZU5hbWVzKCk7XG4gICAgICBjb25zdCB2YWx1ZXMgPSBuZXcgRXhwcmVzc2lvbkF0dHJpYnV0ZVZhbHVlcygpO1xuICAgICAgY29uc3QgZXhwID0gbmV3IENvbmRpdGlvbkV4cHJlc3Npb248eyBmb28/OiBTZXQ8bnVtYmVyPiB8IG51bGwgfT4oXG4gICAgICAgIG5hbWVzLFxuICAgICAgICB2YWx1ZXMsXG4gICAgICApO1xuXG4gICAgICBleHAuY29udGFpbnMoJ2ZvbycsIDMpO1xuXG4gICAgICBhc3NlcnQuZXF1YWwoZXhwLnNlcmlhbGl6ZSgpLCAnY29udGFpbnMoI2ZvbywgOmZvbyknKTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwobmFtZXMuc2VyaWFsaXplKCksIHsgJyNmb28nOiAnZm9vJyB9KTtcbiAgICAgIGFzc2VydC5kZWVwRXF1YWwodmFsdWVzLnNlcmlhbGl6ZSgpLCB7ICc6Zm9vJzogMyB9KTtcbiAgICB9KTtcblxuICAgIGl0KCdvdGhlciBwYXRoJywgKCkgPT4ge1xuICAgICAgY29uc3QgbmFtZXMgPS