UNPKG

ddb-table

Version:

Strongly typed library for querying and modeling DynamoDB documents.

609 lines (607 loc) 89.6 kB
"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