fusion-plugin-rpc
Version:
Fetch data on the server and client with an RPC style interface.
374 lines (371 loc) • 48.5 kB
JavaScript
/** Copyright (c) 2018 Uber Technologies, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
import MockEmitter from 'events';
import App, { createPlugin, createToken } from 'fusion-core';
import { FetchToken } from 'fusion-tokens';
import { getSimulator } from 'fusion-test-utils';
import { UniversalEventsToken } from 'fusion-plugin-universal-events';
import { I18nToken } from 'fusion-plugin-i18n';
import RPCPlugin from '../browser';
import createMockEmitter from './create-mock-emitter';
import { RPCHandlersConfigToken, RPCQueryParamsToken } from '../tokens';
const MockPluginToken = createToken('test-plugin-token');
function createTestFixture() {
const mockFetch = (...args) => Promise.resolve({
json: () => ({
status: 'success',
data: args
})
});
// @ts-ignore
const mockEmitter = new MockEmitter();
const mockEmitterPlugin = createPlugin({
provides: () => mockEmitter
});
const mockI18nPlugin = createPlugin({
provides: () => ({
from: () => ({
locale: 'el-GR',
load: async () => {},
translate: () => ''
})
})
});
const app = new App('content', el => el);
// @ts-ignore
app.register(FetchToken, mockFetch);
app.register(UniversalEventsToken, mockEmitterPlugin);
app.register(I18nToken, mockI18nPlugin);
app.register(MockPluginToken, RPCPlugin);
return app;
}
test('success status request', done => {
const mockEmitter = createMockEmitter({
emit(type, payload) {
expect(type).toBe('rpc:method-client');
expect(payload.method).toBe('test');
expect(payload.status).toBe('success');
expect(typeof payload.timing).toBe('number');
}
});
const app = createTestFixture();
app.register(UniversalEventsToken, mockEmitter);
let wasResolved = false;
getSimulator(app, createPlugin({
deps: {
rpcFactory: MockPluginToken
},
provides: deps => {
const rpc = deps.rpcFactory.from({
memoized: new Map()
});
expect(typeof rpc.request).toBe('function');
expect(rpc.request('test') instanceof Promise).toBeTruthy();
rpc.request('test').then(([url, options]) => {
expect(url).toBe('/api/test?localeCode=el-GR');
expect(options.method).toBe('POST');
expect(options.headers['Content-Type']).toBe('application/json');
expect(options.body).toBe('{}');
done();
}).catch(e => {
// $FlowFixMe
done.fail(e);
});
wasResolved = true;
}
}));
expect(wasResolved).toBeTruthy();
});
test('success status request with additional query params', done => {
const mockEmitter = createMockEmitter({
emit(type, payload) {
expect(type).toBe('rpc:method-client');
expect(payload.method).toBe('test');
expect(payload.status).toBe('success');
expect(typeof payload.timing).toBe('number');
}
});
const app = createTestFixture();
app.register(UniversalEventsToken, mockEmitter);
app.register(RPCQueryParamsToken, {
from() {
return [['hello', 'world']];
}
});
let wasResolved = false;
getSimulator(app, createPlugin({
deps: {
rpcFactory: MockPluginToken
},
provides: deps => {
const rpc = deps.rpcFactory.from({
memoized: new Map()
});
expect(typeof rpc.request).toBe('function');
expect(rpc.request('test') instanceof Promise).toBeTruthy();
rpc.request('test').then(([url, options]) => {
expect(url).toBe('/api/test?hello=world&localeCode=el-GR');
expect(options.method).toBe('POST');
expect(options.headers['Content-Type']).toBe('application/json');
expect(options.body).toBe('{}');
done();
}).catch(e => {
// $FlowFixMe
done.fail(e);
});
wasResolved = true;
}
}));
expect(wasResolved).toBeTruthy();
});
test('success status request (with custom api path)', done => {
const app = createTestFixture();
app.register(RPCHandlersConfigToken, {
apiPath: 'test/api/path'
});
let wasResolved = false;
getSimulator(app, createPlugin({
deps: {
rpcFactory: MockPluginToken
},
provides: deps => {
const rpc = deps.rpcFactory.from({
memoized: new Map()
});
expect(typeof rpc.request).toBe('function');
expect(rpc.request('test') instanceof Promise).toBeTruthy();
rpc.request('test').then(([url, options]) => {
expect(url).toBe('/test/api/path/test?localeCode=el-GR');
expect(options.method).toBe('POST');
expect(options.headers['Content-Type']).toBe('application/json');
expect(options.body).toBe('{}');
done();
}).catch(e => {
// $FlowFixMe
done.fail(e);
});
wasResolved = true;
}
}));
expect(wasResolved).toBeTruthy();
});
test('success status request (with custom api path containing slashes)', done => {
const app = createTestFixture();
app.register(RPCHandlersConfigToken, {
apiPath: '///test/api///path/'
});
let wasResolved = false;
getSimulator(app, createPlugin({
deps: {
rpcFactory: MockPluginToken
},
provides: deps => {
const rpc = deps.rpcFactory.from({
memoized: new Map()
});
expect(typeof rpc.request).toBe('function');
expect(rpc.request('test') instanceof Promise).toBeTruthy();
rpc.request('test').then(([url, options]) => {
expect(url).toBe('/test/api/path/test?localeCode=el-GR');
expect(options.method).toBe('POST');
expect(options.headers['Content-Type']).toBe('application/json');
expect(options.body).toBe('{}');
done();
}).catch(e => {
// $FlowFixMe
done.fail(e);
});
wasResolved = true;
}
}));
expect(wasResolved).toBeTruthy();
});
test('success status request w/args and header', done => {
const mockEmitter = createMockEmitter({
emit(type, payload) {
expect(type).toBe('rpc:method-client');
expect(payload.method).toBe('test');
expect(payload.status).toBe('success');
expect(typeof payload.timing).toBe('number');
}
});
const app = createTestFixture();
// $FlowFixMe
app.register(UniversalEventsToken, mockEmitter);
let wasResolved = false;
getSimulator(app, createPlugin({
deps: {
rpcFactory: MockPluginToken
},
provides: deps => {
const rpc = deps.rpcFactory.from({
memoized: new Map()
});
expect(typeof rpc.request).toBe('function');
expect(rpc.request('test') instanceof Promise).toBeTruthy();
rpc.request('test', {
args: 1
}, {
'test-header': 'header value'
}).then(([url, options]) => {
expect(url).toBe('/api/test?localeCode=el-GR');
expect(options.method).toBe('POST');
expect(options.headers['Content-Type']).toBe('application/json');
expect(options.headers['test-header']).toBe('header value');
expect(options.body).toBe('{"args":1}');
done();
}).catch(e => {
// $FlowFixMe
done.fail(e);
});
wasResolved = true;
}
}));
expect(wasResolved).toBeTruthy();
});
test('success status request w/args and options', done => {
const mockEmitter = createMockEmitter({
emit(type, payload) {
expect(type).toBe('rpc:method-client');
expect(payload.method).toBe('test');
expect(payload.status).toBe('success');
expect(typeof payload.timing).toBe('number');
}
});
const app = createTestFixture();
// $FlowFixMe
app.register(UniversalEventsToken, mockEmitter);
let wasResolved = false;
getSimulator(app, createPlugin({
deps: {
rpcFactory: MockPluginToken
},
provides: deps => {
const rpc = deps.rpcFactory.from({
memoized: new Map()
});
expect(typeof rpc.request).toBe('function');
expect(rpc.request('test') instanceof Promise).toBeTruthy();
rpc.request('test', {
args: 1
}, null, {
credentials: 'omit'
}).then(([url, options]) => {
expect(url).toBe('/api/test?localeCode=el-GR');
expect(options.method).toBe('POST');
expect(options.headers['Content-Type']).toBe('application/json');
expect(options.credentials).toBe('omit');
expect(options.body).toBe('{"args":1}');
done();
}).catch(e => {
// $FlowFixMe
done.fail(e);
});
wasResolved = true;
}
}));
expect(wasResolved).toBeTruthy();
});
test('success status request w/form data', done => {
const mockEmitter = createMockEmitter({
emit(type, payload) {
expect(type).toBe('rpc:method-client');
expect(payload.method).toBe('test');
expect(payload.status).toBe('success');
expect(typeof payload.timing).toBe('number');
}
});
const app = createTestFixture();
// $FlowFixMe
app.register(UniversalEventsToken, mockEmitter);
let wasResolved = false;
getSimulator(app, createPlugin({
deps: {
rpcFactory: MockPluginToken
},
provides: deps => {
const rpc = deps.rpcFactory.from({
memoized: new Map()
});
expect(typeof rpc.request).toBe('function');
expect(rpc.request('test') instanceof Promise).toBeTruthy();
/* eslint-disable cup/no-undef */
const formData = new FormData();
formData.append('random', 'some-random');
formData.append('foo', 'foo content');
const mockFile_1 = new File(['foo'], 'foo.csv', {
type: 'text/csv'
});
const mockFile_2 = new File(['bar'], 'bar.csv', {
type: 'text/csv'
});
formData.append('fooFile', mockFile_1);
formData.append('barFile', mockFile_2);
/* eslint-enable cup/no-undef */
rpc.request('test', formData).then(([url, options]) => {
expect(url).toBe('/api/test?localeCode=el-GR');
expect(options.method).toBe('POST');
expect(options.headers['Content-Type']).toBe(undefined);
// In tests or log, this will show up as `{}`. Don't be fooled~
expect(options.body).toBe(formData);
expect(Array.from(options.body.entries())).toEqual([['random', 'some-random'], ['foo', 'foo content'], ['fooFile', mockFile_1], ['barFile', mockFile_2]]);
done();
}).catch(e => {
// $FlowFixMe
done.fail(e);
});
wasResolved = true;
}
}));
expect(wasResolved).toBeTruthy();
});
test('failure status request', done => {
const mockFetchAsFailure = () => Promise.resolve({
json: () => ({
status: 'failure',
data: 'failure data'
})
});
const mockEmitter = createMockEmitter({
emit(type, payload) {
expect(type).toBe('rpc:method-client');
expect(payload.method).toBe('test');
expect(payload.status).toBe('failure');
expect(typeof payload.timing).toBe('number');
expect(payload.error).toBe('failure data');
}
});
const app = createTestFixture();
// @ts-ignore
app.register(FetchToken, mockFetchAsFailure);
// $FlowFixMe
app.register(UniversalEventsToken, mockEmitter);
let wasResolved = false;
getSimulator(app, createPlugin({
deps: {
rpcFactory: MockPluginToken
},
provides: deps => {
const rpc = deps.rpcFactory.from({
memoized: new Map()
});
expect(typeof rpc.request).toBe('function');
const testRequest = rpc.request('test');
expect(testRequest instanceof Promise).toBeTruthy();
testRequest.then(() => {
// @ts-ignore
done.fail(() => new Error('should reject promise'));
}).catch(e => {
expect(e).toBe('failure data');
done();
});
wasResolved = true;
}
}));
expect(wasResolved).toBeTruthy();
});
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJNb2NrRW1pdHRlciIsIkFwcCIsImNyZWF0ZVBsdWdpbiIsImNyZWF0ZVRva2VuIiwiRmV0Y2hUb2tlbiIsImdldFNpbXVsYXRvciIsIlVuaXZlcnNhbEV2ZW50c1Rva2VuIiwiSTE4blRva2VuIiwiUlBDUGx1Z2luIiwiY3JlYXRlTW9ja0VtaXR0ZXIiLCJSUENIYW5kbGVyc0NvbmZpZ1Rva2VuIiwiUlBDUXVlcnlQYXJhbXNUb2tlbiIsIk1vY2tQbHVnaW5Ub2tlbiIsImNyZWF0ZVRlc3RGaXh0dXJlIiwibW9ja0ZldGNoIiwiYXJncyIsIlByb21pc2UiLCJyZXNvbHZlIiwianNvbiIsInN0YXR1cyIsImRhdGEiLCJtb2NrRW1pdHRlciIsIm1vY2tFbWl0dGVyUGx1Z2luIiwicHJvdmlkZXMiLCJtb2NrSTE4blBsdWdpbiIsImZyb20iLCJsb2NhbGUiLCJsb2FkIiwidHJhbnNsYXRlIiwiYXBwIiwiZWwiLCJyZWdpc3RlciIsInRlc3QiLCJkb25lIiwiZW1pdCIsInR5cGUiLCJwYXlsb2FkIiwiZXhwZWN0IiwidG9CZSIsIm1ldGhvZCIsInRpbWluZyIsIndhc1Jlc29sdmVkIiwiZGVwcyIsInJwY0ZhY3RvcnkiLCJycGMiLCJtZW1vaXplZCIsIk1hcCIsInJlcXVlc3QiLCJ0b0JlVHJ1dGh5IiwidGhlbiIsInVybCIsIm9wdGlvbnMiLCJoZWFkZXJzIiwiYm9keSIsImNhdGNoIiwiZSIsImZhaWwiLCJhcGlQYXRoIiwiY3JlZGVudGlhbHMiLCJmb3JtRGF0YSIsIkZvcm1EYXRhIiwiYXBwZW5kIiwibW9ja0ZpbGVfMSIsIkZpbGUiLCJtb2NrRmlsZV8yIiwidW5kZWZpbmVkIiwiQXJyYXkiLCJlbnRyaWVzIiwidG9FcXVhbCIsIm1vY2tGZXRjaEFzRmFpbHVyZSIsImVycm9yIiwidGVzdFJlcXVlc3QiLCJFcnJvciJdLCJzb3VyY2VzIjpbInNyYy9fX3Rlc3RzX18vaW5kZXguYnJvd3Nlci50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKiogQ29weXJpZ2h0IChjKSAyMDE4IFViZXIgVGVjaG5vbG9naWVzLCBJbmMuXG4gKlxuICogVGhpcyBzb3VyY2UgY29kZSBpcyBsaWNlbnNlZCB1bmRlciB0aGUgTUlUIGxpY2Vuc2UgZm91bmQgaW4gdGhlXG4gKiBMSUNFTlNFIGZpbGUgaW4gdGhlIHJvb3QgZGlyZWN0b3J5IG9mIHRoaXMgc291cmNlIHRyZWUuXG4gKlxuICovXG5cbmltcG9ydCBNb2NrRW1pdHRlciBmcm9tICdldmVudHMnO1xuXG5pbXBvcnQgQXBwLCB7Y3JlYXRlUGx1Z2luLCBjcmVhdGVUb2tlbn0gZnJvbSAnZnVzaW9uLWNvcmUnO1xuaW1wb3J0IHtGZXRjaFRva2VufSBmcm9tICdmdXNpb24tdG9rZW5zJztcbmltcG9ydCB7Z2V0U2ltdWxhdG9yfSBmcm9tICdmdXNpb24tdGVzdC11dGlscyc7XG5pbXBvcnQgdHlwZSB7VG9rZW59IGZyb20gJ2Z1c2lvbi1jb3JlJztcbmltcG9ydCB7VW5pdmVyc2FsRXZlbnRzVG9rZW59IGZyb20gJ2Z1c2lvbi1wbHVnaW4tdW5pdmVyc2FsLWV2ZW50cyc7XG5pbXBvcnQge0kxOG5Ub2tlbn0gZnJvbSAnZnVzaW9uLXBsdWdpbi1pMThuJztcblxuaW1wb3J0IFJQQ1BsdWdpbiBmcm9tICcuLi9icm93c2VyJztcbmltcG9ydCB0eXBlIHtJRW1pdHRlcn0gZnJvbSAnLi4vdHlwZXMnO1xuaW1wb3J0IGNyZWF0ZU1vY2tFbWl0dGVyIGZyb20gJy4vY3JlYXRlLW1vY2stZW1pdHRlcic7XG5pbXBvcnQge1JQQ0hhbmRsZXJzQ29uZmlnVG9rZW4sIFJQQ1F1ZXJ5UGFyYW1zVG9rZW59IGZyb20gJy4uL3Rva2Vucyc7XG5cbmNvbnN0IE1vY2tQbHVnaW5Ub2tlbjogVG9rZW48YW55PiA9IGNyZWF0ZVRva2VuKCd0ZXN0LXBsdWdpbi10b2tlbicpO1xuZnVuY3Rpb24gY3JlYXRlVGVzdEZpeHR1cmUoKSB7XG4gIGNvbnN0IG1vY2tGZXRjaCA9ICguLi5hcmdzKSA9PlxuICAgIFByb21pc2UucmVzb2x2ZSh7anNvbjogKCkgPT4gKHtzdGF0dXM6ICdzdWNjZXNzJywgZGF0YTogYXJnc30pfSk7XG4gIC8vIEB0cy1pZ25vcmVcbiAgY29uc3QgbW9ja0VtaXR0ZXI6IElFbWl0dGVyID0gbmV3IE1vY2tFbWl0dGVyKCkgYXMgYW55O1xuICBjb25zdCBtb2NrRW1pdHRlclBsdWdpbiA9IGNyZWF0ZVBsdWdpbih7XG4gICAgcHJvdmlkZXM6ICgpID0+IG1vY2tFbWl0dGVyLFxuICB9KTtcbiAgY29uc3QgbW9ja0kxOG5QbHVnaW4gPSBjcmVhdGVQbHVnaW4oe1xuICAgIHByb3ZpZGVzOiAoKSA9PiAoe1xuICAgICAgZnJvbTogKCkgPT4gKHtcbiAgICAgICAgbG9jYWxlOiAnZWwtR1InLFxuICAgICAgICBsb2FkOiBhc3luYyAoKSA9PiB7fSxcbiAgICAgICAgdHJhbnNsYXRlOiAoKSA9PiAnJyxcbiAgICAgIH0pLFxuICAgIH0pLFxuICB9KTtcblxuICBjb25zdCBhcHAgPSBuZXcgQXBwKCdjb250ZW50JywgKGVsKSA9PiBlbCk7XG4gIC8vIEB0cy1pZ25vcmVcbiAgYXBwLnJlZ2lzdGVyKEZldGNoVG9rZW4sIG1vY2tGZXRjaCk7XG4gIGFwcC5yZWdpc3RlcihVbml2ZXJzYWxFdmVudHNUb2tlbiwgbW9ja0VtaXR0ZXJQbHVnaW4pO1xuICBhcHAucmVnaXN0ZXIoSTE4blRva2VuLCBtb2NrSTE4blBsdWdpbik7XG4gIGFwcC5yZWdpc3RlcihNb2NrUGx1Z2luVG9rZW4sIFJQQ1BsdWdpbik7XG4gIHJldHVybiBhcHA7XG59XG5cbnRlc3QoJ3N1Y2Nlc3Mgc3RhdHVzIHJlcXVlc3QnLCAoZG9uZSkgPT4ge1xuICBjb25zdCBtb2NrRW1pdHRlciA9IGNyZWF0ZU1vY2tFbWl0dGVyKHtcbiAgICBlbWl0KHR5cGUsIHBheWxvYWQpIHtcbiAgICAgIGV4cGVjdCh0eXBlKS50b0JlKCdycGM6bWV0aG9kLWNsaWVudCcpO1xuICAgICAgZXhwZWN0KHBheWxvYWQubWV0aG9kKS50b0JlKCd0ZXN0Jyk7XG4gICAgICBleHBlY3QocGF5bG9hZC5zdGF0dXMpLnRvQmUoJ3N1Y2Nlc3MnKTtcbiAgICAgIGV4cGVjdCh0eXBlb2YgcGF5bG9hZC50aW1pbmcpLnRvQmUoJ251bWJlcicpO1xuICAgIH0sXG4gIH0pO1xuICBjb25zdCBhcHAgPSBjcmVhdGVUZXN0Rml4dHVyZSgpO1xuICBhcHAucmVnaXN0ZXIoVW5pdmVyc2FsRXZlbnRzVG9rZW4sIG1vY2tFbWl0dGVyKTtcblxuICBsZXQgd2FzUmVzb2x2ZWQgPSBmYWxzZTtcbiAgZ2V0U2ltdWxhdG9yKFxuICAgIGFwcCxcbiAgICBjcmVhdGVQbHVnaW4oe1xuICAgICAgZGVwczoge3JwY0ZhY3Rvcnk6IE1vY2tQbHVnaW5Ub2tlbn0sXG4gICAgICBwcm92aWRlczogKGRlcHMpID0+IHtcbiAgICAgICAgY29uc3QgcnBjID0gZGVwcy5ycGNGYWN0b3J5LmZyb20oe1xuICAgICAgICAgIG1lbW9pemVkOiBuZXcgTWFwKCksXG4gICAgICAgIH0pO1xuICAgICAgICBleHBlY3QodHlwZW9mIHJwYy5yZXF1ZXN0KS50b0JlKCdmdW5jdGlvbicpO1xuICAgICAgICBleHBlY3QocnBjLnJlcXVlc3QoJ3Rlc3QnKSBpbnN0YW5jZW9mIFByb21pc2UpLnRvQmVUcnV0aHkoKTtcbiAgICAgICAgcnBjXG4gICAgICAgICAgLnJlcXVlc3QoJ3Rlc3QnKVxuICAgICAgICAgIC50aGVuKChbdXJsLCBvcHRpb25zXSkgPT4ge1xuICAgICAgICAgICAgZXhwZWN0KHVybCkudG9CZSgnL2FwaS90ZXN0P2xvY2FsZUNvZGU9ZWwtR1InKTtcbiAgICAgICAgICAgIGV4cGVjdChvcHRpb25zLm1ldGhvZCkudG9CZSgnUE9TVCcpO1xuICAgICAgICAgICAgZXhwZWN0KG9wdGlvbnMuaGVhZGVyc1snQ29udGVudC1UeXBlJ10pLnRvQmUoJ2FwcGxpY2F0aW9uL2pzb24nKTtcbiAgICAgICAgICAgIGV4cGVjdChvcHRpb25zLmJvZHkpLnRvQmUoJ3t9Jyk7XG4gICAgICAgICAgICBkb25lKCk7XG4gICAgICAgICAgfSlcbiAgICAgICAgICAuY2F0Y2goKGUpID0+IHtcbiAgICAgICAgICAgIC8vICRGbG93Rml4TWVcbiAgICAgICAgICAgIGRvbmUuZmFpbChlKTtcbiAgICAgICAgICB9KTtcblxuICAgICAgICB3YXNSZXNvbHZlZCA9IHRydWU7XG4gICAgICB9LFxuICAgIH0pXG4gICk7XG5cbiAgZXhwZWN0KHdhc1Jlc29sdmVkKS50b0JlVHJ1dGh5KCk7XG59KTtcblxudGVzdCgnc3VjY2VzcyBzdGF0dXMgcmVxdWVzdCB3aXRoIGFkZGl0aW9uYWwgcXVlcnkgcGFyYW1zJywgKGRvbmUpID0+IHtcbiAgY29uc3QgbW9ja0VtaXR0ZXIgPSBjcmVhdGVNb2NrRW1pdHRlcih7XG4gICAgZW1pdCh0eXBlLCBwYXlsb2FkKSB7XG4gICAgICBleHBlY3QodHlwZSkudG9CZSgncnBjOm1ldGhvZC1jbGllbnQnKTtcbiAgICAgIGV4cGVjdChwYXlsb2FkLm1ldGhvZCkudG9CZSgndGVzdCcpO1xuICAgICAgZXhwZWN0KHBheWxvYWQuc3RhdHVzKS50b0JlKCdzdWNjZXNzJyk7XG4gICAgICBleHBlY3QodHlwZW9mIHBheWxvYWQudGltaW5nKS50b0JlKCdudW1iZXInKTtcbiAgICB9LFxuICB9KTtcbiAgY29uc3QgYXBwID0gY3JlYXRlVGVzdEZpeHR1cmUoKTtcbiAgYXBwLnJlZ2lzdGVyKFVuaXZlcnNhbEV2ZW50c1Rva2VuLCBtb2NrRW1pdHRlcik7XG4gIGFwcC5yZWdpc3RlcihSUENRdWVyeVBhcmFtc1Rva2VuLCB7XG4gICAgZnJvbSgpIHtcbiAgICAgIHJldHVybiBbWydoZWxsbycsICd3b3JsZCddXTtcbiAgICB9LFxuICB9KTtcblxuICBsZXQgd2FzUmVzb2x2ZWQgPSBmYWxzZTtcbiAgZ2V0U2ltdWxhdG9yKFxuICAgIGFwcCxcbiAgICBjcmVhdGVQbHVnaW4oe1xuICAgICAgZGVwczoge3JwY0ZhY3Rvcnk6IE1vY2tQbHVnaW5Ub2tlbn0sXG4gICAgICBwcm92aWRlczogKGRlcHMpID0+IHtcbiAgICAgICAgY29uc3QgcnBjID0gZGVwcy5ycGNGYWN0b3J5LmZyb20oe1xuICAgICAgICAgIG1lbW9pemVkOiBuZXcgTWFwKCksXG4gICAgICAgIH0pO1xuICAgICAgICBleHBlY3QodHlwZW9mIHJwYy5yZXF1ZXN0KS50b0JlKCdmdW5jdGlvbicpO1xuICAgICAgICBleHBlY3QocnBjLnJlcXVlc3QoJ3Rlc3QnKSBpbnN0YW5jZW9mIFByb21pc2UpLnRvQmVUcnV0aHkoKTtcbiAgICAgICAgcnBjXG4gICAgICAgICAgLnJlcXVlc3QoJ3Rlc3QnKVxuICAgICAgICAgIC50aGVuKChbdXJsLCBvcHRpb25zXSkgPT4ge1xuICAgICAgICAgICAgZXhwZWN0KHVybCkudG9CZSgnL2FwaS90ZXN0P2hlbGxvPXdvcmxkJmxvY2FsZUNvZGU9ZWwtR1InKTtcbiAgICAgICAgICAgIGV4cGVjdChvcHRpb25zLm1ldGhvZCkudG9CZSgnUE9TVCcpO1xuICAgICAgICAgICAgZXhwZWN0KG9wdGlvbnMuaGVhZGVyc1snQ29udGVudC1UeXBlJ10pLnRvQmUoJ2FwcGxpY2F0aW9uL2pzb24nKTtcbiAgICAgICAgICAgIGV4cGVjdChvcHRpb25zLmJvZHkpLnRvQmUoJ3t9Jyk7XG4gICAgICAgICAgICBkb25lKCk7XG4gICAgICAgICAgfSlcbiAgICAgICAgICAuY2F0Y2goKGUpID0+IHtcbiAgICAgICAgICAgIC8vICRGbG93Rml4TWVcbiAgICAgICAgICAgIGRvbmUuZmFpbChlKTtcbiAgICAgICAgICB9KTtcblxuICAgICAgICB3YXNSZXNvbHZlZCA9IHRydWU7XG4gICAgICB9LFxuICAgIH0pXG4gICk7XG5cbiAgZXhwZWN0KHdhc1Jlc29sdmVkKS50b0JlVHJ1dGh5KCk7XG59KTtcblxudGVzdCgnc3VjY2VzcyBzdGF0dXMgcmVxdWVzdCAod2l0aCBjdXN0b20gYXBpIHBhdGgpJywgKGRvbmUpID0+IHtcbiAgY29uc3QgYXBwID0gY3JlYXRlVGVzdEZpeHR1cmUoKTtcblxuICBhcHAucmVnaXN0ZXIoUlBDSGFuZGxlcnNDb25maWdUb2tlbiwge2FwaVBhdGg6ICd0ZXN0L2FwaS9wYXRoJ30pO1xuXG4gIGxldCB3YXNSZXNvbHZlZCA9IGZhbHNlO1xuICBnZXRTaW11bGF0b3IoXG4gICAgYXBwLFxuICAgIGNyZWF0ZVBsdWdpbih7XG4gICAgICBkZXBzOiB7cnBjRmFjdG9yeTogTW9ja1BsdWdpblRva2VufSxcbiAgICAgIHByb3ZpZGVzOiAoZGVwcykgPT4ge1xuICAgICAgICBjb25zdCBycGMgPSBkZXBzLnJwY0ZhY3RvcnkuZnJvbSh7XG4gICAgICAgICAgbWVtb2l6ZWQ6IG5ldyBNYXAoKSxcbiAgICAgICAgfSk7XG4gICAgICAgIGV4cGVjdCh0eXBlb2YgcnBjLnJlcXVlc3QpLnRvQmUoJ2Z1bmN0aW9uJyk7XG4gICAgICAgIGV4cGVjdChycGMucmVxdWVzdCgndGVzdCcpIGluc3RhbmNlb2YgUHJvbWlzZSkudG9CZVRydXRoeSgpO1xuICAgICAgICBycGNcbiAgICAgICAgICAucmVxdWVzdCgndGVzdCcpXG4gICAgICAgICAgLnRoZW4oKFt1cmwsIG9wdGlvbnNdKSA9PiB7XG4gICAgICAgICAgICBleHBlY3QodXJsKS50b0JlKCcvdGVzdC9hcGkvcGF0aC90ZXN0P2xvY2FsZUNvZGU9ZWwtR1InKTtcbiAgICAgICAgICAgIGV4cGVjdChvcHRpb25zLm1ldGhvZCkudG9CZSgnUE9TVCcpO1xuICAgICAgICAgICAgZXhwZWN0KG9wdGlvbnMuaGVhZGVyc1snQ29udGVudC1UeXBlJ10pLnRvQmUoJ2FwcGxpY2F0aW9uL2pzb24nKTtcbiAgICAgICAgICAgIGV4cGVjdChvcHRpb25zLmJvZHkpLnRvQmUoJ3t9Jyk7XG4gICAgICAgICAgICBkb25lKCk7XG4gICAgICAgICAgfSlcbiAgICAgICAgICAuY2F0Y2goKGUpID0+IHtcbiAgICAgICAgICAgIC8vICRGbG93Rml4TWVcbiAgICAgICAgICAgIGRvbmUuZmFpbChlKTtcbiAgICAgICAgICB9KTtcblxuICAgICAgICB3YXNSZXNvbHZlZCA9IHRydWU7XG4gICAgICB9LFxuICAgIH0pXG4gICk7XG5cbiAgZXhwZWN0KHdhc1Jlc29sdmVkKS50b0JlVHJ1dGh5KCk7XG59KTtcblxudGVzdCgnc3VjY2VzcyBzdGF0dXMgcmVxdWVzdCAod2l0aCBjdXN0b20gYXBpIHBhdGggY29udGFpbmluZyBzbGFzaGVzKScsIChkb25lKSA9PiB7XG4gIGNvbnN0IGFwcCA9IGNyZWF0ZVRlc3RGaXh0dXJlKCk7XG5cbiAgYXBwLnJlZ2lzdGVyKFJQQ0hhbmRsZXJzQ29uZmlnVG9rZW4sIHthcGlQYXRoOiAnLy8vdGVzdC9hcGkvLy9wYXRoLyd9KTtcblxuICBsZXQgd2FzUmVzb2x2ZWQgPSBmYWxzZTtcbiAgZ2V0U2ltdWxhdG9yKFxuICAgIGFwcCxcbiAgICBjcmVhdGVQbHVnaW4oe1xuICAgICAgZGVwczoge3JwY0ZhY3Rvcnk6IE1vY2tQbHVnaW5Ub2tlbn0sXG4gICAgICBwcm92aWRlczogKGRlcHMpID0+IHtcbiAgICAgICAgY29uc3QgcnBjID0gZGVwcy5ycGNGYWN0b3J5LmZyb20oe1xuICAgICAgICAgIG1lbW9pemVkOiBuZXcgTWFwKCksXG4gICAgICAgIH0pO1xuICAgICAgICBleHBlY3QodHlwZW9mIHJwYy5yZXF1ZXN0KS50b0JlKCdmdW5jdGlvbicpO1xuICAgICAgICBleHBlY3QocnBjLnJlcXVlc3QoJ3Rlc3QnKSBpbnN0YW5jZW9mIFByb21pc2UpLnRvQmVUcnV0aHkoKTtcbiAgICAgICAgcnBjXG4gICAgICAgICAgLnJlcXVlc3QoJ3Rlc3QnKVxuICAgICAgICAgIC50aGVuKChbdXJsLCBvcHRpb25zXSkgPT4ge1xuICAgICAgICAgICAgZXhwZWN0KHVybCkudG9CZSgnL3Rlc3QvYXBpL3BhdGgvdGVzdD9sb2NhbGVDb2RlPWVsLUdSJyk7XG4gICAgICAgICAgICBleHBlY3Qob3B0aW9ucy5tZXRob2QpLnRvQmUoJ1BPU1QnKTtcbiAgICAgICAgICAgIGV4cGVjdChvcHRpb25zLmhlYWRlcnNbJ0NvbnRlbnQtVHlwZSddKS50b0JlKCdhcHBsaWNhdGlvbi9qc29uJyk7XG4gICAgICAgICAgICBleHBlY3Qob3B0aW9ucy5ib2R5KS50b0JlKCd7fScpO1xuICAgICAgICAgICAgZG9uZSgpO1xuICAgICAgICAgIH0pXG4gICAgICAgICAgLmNhdGNoKChlKSA9PiB7XG4gICAgICAgICAgICAvLyAkRmxvd0ZpeE1lXG4gICAgICAgICAgICBkb25lLmZhaWwoZSk7XG4gICAgICAgICAgfSk7XG5cbiAgICAgICAgd2FzUmVzb2x2ZWQgPSB0cnVlO1xuICAgICAgfSxcbiAgICB9KVxuICApO1xuXG4gIGV4cGVjdCh3YXNSZXNvbHZlZCkudG9CZVRydXRoeSgpO1xufSk7XG5cbnRlc3QoJ3N1Y2Nlc3Mgc3RhdHVzIHJlcXVlc3Qgdy9hcmdzIGFuZCBoZWFkZXInLCAoZG9uZSkgPT4ge1xuICBjb25zdCBtb2NrRW1pdHRlciA9IGNyZWF0ZU1vY2tFbWl0dGVyKHtcbiAgICBlbWl0KHR5cGUsIHBheWxvYWQpIHtcbiAgICAgIGV4cGVjdCh0eXBlKS50b0JlKCdycGM6bWV0aG9kLWNsaWVudCcpO1xuICAgICAgZXhwZWN0KHBheWxvYWQubWV0aG9kKS50b0JlKCd0ZXN0Jyk7XG4gICAgICBleHBlY3QocGF5bG9hZC5zdGF0dXMpLnRvQmUoJ3N1Y2Nlc3MnKTtcbiAgICAgIGV4cGVjdCh0eXBlb2YgcGF5bG9hZC50aW1pbmcpLnRvQmUoJ251bWJlcicpO1xuICAgIH0sXG4gIH0pO1xuICBjb25zdCBhcHAgPSBjcmVhdGVUZXN0Rml4dHVyZSgpO1xuICAvLyAkRmxvd0ZpeE1lXG4gIGFwcC5yZWdpc3RlcihVbml2ZXJzYWxFdmVudHNUb2tlbiwgbW9ja0VtaXR0ZXIpO1xuXG4gIGxldCB3YXNSZXNvbHZlZCA9IGZhbHNlO1xuICBnZXRTaW11bGF0b3IoXG4gICAgYXBwLFxuICAgIGNyZWF0ZVBsdWdpbih7XG4gICAgICBkZXBzOiB7cnBjRmFjdG9yeTogTW9ja1BsdWdpblRva2VufSxcbiAgICAgIHByb3ZpZGVzOiAoZGVwcykgPT4ge1xuICAgICAgICBjb25zdCBycGMgPSBkZXBzLnJwY0ZhY3RvcnkuZnJvbSh7XG4gICAgICAgICAgbWVtb2l6ZWQ6IG5ldyBNYXAoKSxcbiAgICAgICAgfSk7XG4gICAgICAgIGV4cGVjdCh0eXBlb2YgcnBjLnJlcXVlc3QpLnRvQmUoJ2Z1bmN0aW9uJyk7XG4gICAgICAgIGV4cGVjdChycGMucmVxdWVzdCgndGVzdCcpIGluc3RhbmNlb2YgUHJvbWlzZSkudG9CZVRydXRoeSgpO1xuICAgICAgICBycGNcbiAgICAgICAgICAucmVxdWVzdCgndGVzdCcsIHthcmdzOiAxfSwgeyd0ZXN0LWhlYWRlcic6ICdoZWFkZXIgdmFsdWUnfSlcbiAgICAgICAgICAudGhlbigoW3VybCwgb3B0aW9uc10pID0+IHtcbiAgICAgICAgICAgIGV4cGVjdCh1cmwpLnRvQmUoJy9hcGkvdGVzdD9sb2NhbGVDb2RlPWVsLUdSJyk7XG4gICAgICAgICAgICBleHBlY3Qob3B0aW9ucy5tZXRob2QpLnRvQmUoJ1BPU1QnKTtcbiAgICAgICAgICAgIGV4cGVjdChvcHRpb25zLmhlYWRlcnNbJ0NvbnRlbnQtVHlwZSddKS50b0JlKCdhcHBsaWNhdGlvbi9qc29uJyk7XG4gICAgICAgICAgICBleHBlY3Qob3B0aW9ucy5oZWFkZXJzWyd0ZXN0LWhlYWRlciddKS50b0JlKCdoZWFkZXIgdmFsdWUnKTtcbiAgICAgICAgICAgIGV4cGVjdChvcHRpb25zLmJvZHkpLnRvQmUoJ3tcImFyZ3NcIjoxfScpO1xuICAgICAgICAgICAgZG9uZSgpO1xuICAgICAgICAgIH0pXG4gICAgICAgICAgLmNhdGNoKChlKSA9PiB7XG4gICAgICAgICAgICAvLyAkRmxvd0ZpeE1lXG4gICAgICAgICAgICBkb25lLmZhaWwoZSk7XG4gICAgICAgICAgfSk7XG5cbiAgICAgICAgd2FzUmVzb2x2ZWQgPSB0cnVlO1xuICAgICAgfSxcbiAgICB9KVxuICApO1xuXG4gIGV4cGVjdCh3YXNSZXNvbHZlZCkudG9CZVRydXRoeSgpO1xufSk7XG5cbnRlc3QoJ3N1Y2Nlc3Mgc3RhdHVzIHJlcXVlc3Qgdy9hcmdzIGFuZCBvcHRpb25zJywgKGRvbmUpID0+IHtcbiAgY29uc3QgbW9ja0VtaXR0ZXIgPSBjcmVhdGVNb2NrRW1pdHRlcih7XG4gICAgZW1pdCh0eXBlLCBwYXlsb2FkKSB7XG4gICAgICBleHBlY3QodHlwZSkudG9CZSgncnBjOm1ldGhvZC1jbGllbnQnKTtcbiAgICAgIGV4cGVjdChwYXlsb2FkLm1ldGhvZCkudG9CZSgndGVzdCcpO1xuICAgICAgZXhwZWN0KHBheWxvYWQuc3RhdHVzKS50b0JlKCdzdWNjZXNzJyk7XG4gICAgICBleHBlY3QodHlwZW9mIHBheWxvYWQudGltaW5nKS50b0JlKCdudW1iZXInKTtcbiAgICB9LFxuICB9KTtcbiAgY29uc3QgYXBwID0gY3JlYXRlVGVzdEZpeHR1cmUoKTtcbiAgLy8gJEZsb3dGaXhNZVxuICBhcHAucmVnaXN0ZXIoVW5pdmVyc2FsRXZlbnRzVG9rZW4sIG1vY2tFbWl0dGVyKTtcblxuICBsZXQgd2FzUmVzb2x2ZWQgPSBmYWxzZTtcbiAgZ2V0U2ltdWxhdG9yKFxuICAgIGFwcCxcbiAgICBjcmVhdGVQbHVnaW4oe1xuICAgICAgZGVwczoge3JwY0ZhY3Rvcnk6IE1vY2tQbHVnaW5Ub2tlbn0sXG4gICAgICBwcm92aWRlczogKGRlcHMpID0+IHtcbiAgICAgICAgY29uc3QgcnBjID0gZGVwcy5ycGNGYWN0b3J5LmZyb20oe1xuICAgICAgICAgIG1lbW9pemVkOiBuZXcgTWFwKCksXG4gICAgICAgIH0pO1xuICAgICAgICBleHBlY3QodHlwZW9mIHJwYy5yZXF1ZXN0KS50b0JlKCdmdW5jdGlvbicpO1xuICAgICAgICBleHBlY3QocnBjLnJlcXVlc3QoJ3Rlc3QnKSBpbnN0YW5jZW9mIFByb21pc2UpLnRvQmVUcnV0aHkoKTtcbiAgICAgICAgcnBjXG4gICAgICAgICAgLnJlcXVlc3QoJ3Rlc3QnLCB7YXJnczogMX0sIG51bGwsIHtjcmVkZW50aWFsczogJ29taXQnfSlcbiAgICAgICAgICAudGhlbigoW3VybCwgb3B0aW9uc10pID0+IHtcbiAgICAgICAgICAgIGV4cGVjdCh1cmwpLnRvQmUoJy9hcGkvdGVzdD9sb2NhbGVDb2RlPWVsLUdSJyk7XG4gICAgICAgICAgICBleHBlY3Qob3B0aW9ucy5tZXRob2QpLnRvQmUoJ1BPU1QnKTtcbiAgICAgICAgICAgIGV4cGVjdChvcHRpb25zLmhlYWRlcnNbJ0NvbnRlbnQtVHlwZSddKS50b0JlKCdhcHBsaWNhdGlvbi9qc29uJyk7XG4gICAgICAgICAgICBleHBlY3Qob3B0aW9ucy5jcmVkZW50aWFscykudG9CZSgnb21pdCcpO1xuICAgICAgICAgICAgZXhwZWN0KG9wdGlvbnMuYm9keSkudG9CZSgne1wiYXJnc1wiOjF9Jyk7XG4gICAgICAgICAgICBkb25lKCk7XG4gICAgICAgICAgfSlcbiAgICAgICAgICAuY2F0Y2goKGUpID0+IHtcbiAgICAgICAgICAgIC8vICRGbG93Rml4TWVcbiAgICAgICAgICAgIGRvbmUuZmFpbChlKTtcbiAgICAgICAgICB9KTtcblxuICAgICAgICB3YXNSZXNvbHZlZCA9IHRydWU7XG4gICAgICB9LFxuICAgIH0pXG4gICk7XG5cbiAgZXhwZWN0KHdhc1Jlc29sdmVkKS50b0JlVHJ1dGh5KCk7XG59KTtcblxudGVzdCgnc3VjY2VzcyBzdGF0dXMgcmVxdWVzdCB3L2Zvcm0gZGF0YScsIChkb25lKSA9PiB7XG4gIGNvbnN0IG1vY2tFbWl0dGVyID0gY3JlYXRlTW9ja0VtaXR0ZXIoe1xuICAgIGVtaXQodHlwZSwgcGF5bG9hZCkge1xuICAgICAgZXhwZWN0KHR5cGUpLnRvQmUoJ3JwYzptZXRob2QtY2xpZW50Jyk7XG4gICAgICBleHBlY3QocGF5bG9hZC5tZXRob2QpLnRvQmUoJ3Rlc3QnKTtcbiAgICAgIGV4cGVjdChwYXlsb2FkLnN0YXR1cykudG9CZSgnc3VjY2VzcycpO1xuICAgICAgZXhwZWN0KHR5cGVvZiBwYXlsb2FkLnRpbWluZykudG9CZSgnbnVtYmVyJyk7XG4gICAgfSxcbiAgfSk7XG4gIGNvbnN0IGFwcCA9IGNyZWF0ZVRlc3RGaXh0dXJlKCk7XG4gIC8vICRGbG93Rml4TWVcbiAgYXBwLnJlZ2lzdGVyKFVuaXZlcnNhbEV2ZW50c1Rva2VuLCBtb2NrRW1pdHRlcik7XG5cbiAgbGV0IHdhc1Jlc29sdmVkID0gZmFsc2U7XG4gIGdldFNpbXVsYXRvcihcbiAgICBhcHAsXG4gICAgY3JlYXRlUGx1Z2luKHtcbiAgICAgIGRlcHM6IHtycGNGYWN0b3J5OiBNb2NrUGx1Z2luVG9rZW59LFxuICAgICAgcHJvdmlkZXM6IChkZXBzKSA9PiB7XG4gICAgICAgIGNvbnN0IHJwYyA9IGRlcHMucnBjRmFjdG9yeS5mcm9tKHtcbiAgICAgICAgICBtZW1vaXplZDogbmV3IE1hcCgpLFxuICAgICAgICB9KTtcbiAgICAgICAgZXhwZWN0KHR5cGVvZiBycGMucmVxdWVzdCkudG9CZSgnZnVuY3Rpb24nKTtcbiAgICAgICAgZXhwZWN0KHJwYy5yZXF1ZXN0KCd0ZXN0JykgaW5zdGFuY2VvZiBQcm9taXNlKS50b0JlVHJ1dGh5KCk7XG4gICAgICAgIC8qIGVzbGludC1kaXNhYmxlIGN1cC9uby11bmRlZiAqL1xuICAgICAgICBjb25zdCBmb3JtRGF0YSA9IG5ldyBGb3JtRGF0YSgpO1xuICAgICAgICBmb3JtRGF0YS5hcHBlbmQoJ3JhbmRvbScsICdzb21lLXJhbmRvbScpO1xuICAgICAgICBmb3JtRGF0YS5hcHBlbmQoJ2ZvbycsICdmb28gY29udGVudCcpO1xuXG4gICAgICAgIGNvbnN0IG1vY2tGaWxlXzEgPSBuZXcgRmlsZShbJ2ZvbyddLCAnZm9vLmNzdicsIHt0eXBlOiAndGV4dC9jc3YnfSk7XG4gICAgICAgIGNvbnN0IG1vY2tGaWxlXzIgPSBuZXcgRmlsZShbJ2JhciddLCAnYmFyLmNzdicsIHt0eXBlOiAndGV4dC9jc3YnfSk7XG5cbiAgICAgICAgZm9ybURhdGEuYXBwZW5kKCdmb29GaWxlJywgbW9ja0ZpbGVfMSk7XG4gICAgICAgIGZvcm1EYXRhLmFwcGVuZCgnYmFyRmlsZScsIG1vY2tGaWxlXzIpO1xuXG4gICAgICAgIC8qIGVzbGludC1lbmFibGUgY3VwL25vLXVuZGVmICovXG5cbiAgICAgICAgcnBjXG4gICAgICAgICAgLnJlcXVlc3QoJ3Rlc3QnLCBmb3JtRGF0YSlcbiAgICAgICAgICAudGhlbigoW3VybCwgb3B0aW9uc10pID0+IHtcbiAgICAgICAgICAgIGV4cGVjdCh1cmwpLnRvQmUoJy9hcGkvdGVzdD9sb2NhbGVDb2RlPWVsLUdSJyk7XG4gICAgICAgICAgICBleHBlY3Qob3B0aW9ucy5tZXRob2QpLnRvQmUoJ1BPU1QnKTtcbiAgICAgICAgICAgIGV4cGVjdChvcHRpb25zLmhlYWRlcnNbJ0NvbnRlbnQtVHlwZSddKS50b0JlKHVuZGVmaW5lZCk7XG4gICAgICAgICAgICAvLyBJbiB0ZXN0cyBvciBsb2csIHRoaXMgd2lsbCBzaG93IHVwIGFzIGB7fWAuIERvbid0IGJlIGZvb2xlZH5cbiAgICAgICAgICAgIGV4cGVjdChvcHRpb25zLmJvZHkpLnRvQmUoZm9ybURhdGEpO1xuICAgICAgICAgICAgZXhwZWN0KEFycmF5LmZyb20ob3B0aW9ucy5ib2R5LmVudHJpZXMoKSkpLnRvRXF1YWwoW1xuICAgICAgICAgICAgICBbJ3JhbmRvbScsICdzb21lLXJhbmRvbSddLFxuICAgICAgICAgICAgICBbJ2ZvbycsICdmb28gY29udGVudCddLFxuICAgICAgICAgICAgICBbJ2Zvb0ZpbGUnLCBtb2NrRmlsZV8xXSxcbiAgICAgICAgICAgICAgWydiYXJGaWxlJywgbW9ja0ZpbGVfMl0sXG4gICAgICAgICAgICBdKTtcbiAgICAgICAgICAgIGRvbmUoKTtcbiAgICAgICAgICB9KVxuICAgICAgICAgIC5jYXRjaCgoZSkgPT4ge1xuICAgICAgICAgICAgLy8gJEZsb3dGaXhNZVxuICAgICAgICAgICAgZG9uZS5mYWlsKGUpO1xuICAgICAgICAgIH0pO1xuXG4gICAgICAgIHdhc1Jlc29sdmVkID0gdHJ1ZTtcbiAgICAgIH0sXG4gICAgfSlcbiAgKTtcblxuICBleHBlY3Qod2FzUmVzb2x2ZWQpLnRvQmVUcnV0aHkoKTtcbn0pO1xuXG50ZXN0KCdmYWlsdXJlIHN0YXR1cyByZXF1ZXN0JywgKGRvbmUpID0+IHtcbiAgY29uc3QgbW9ja0ZldGNoQXNGYWlsdXJlID0gKCkgPT5cbiAgICBQcm9taXNlLnJlc29sdmUoe1xuICAgICAganNvbjogKCkgPT4gKHtzdGF0dXM6ICdmYWlsdXJlJywgZGF0YTogJ2ZhaWx1cmUgZGF0YSd9KSxcbiAgICB9KTtcbiAgY29uc3QgbW9ja0VtaXR0ZXIgPSBjcmVhdGVNb2NrRW1pdHRlcih7XG4gICAgZW1pdCh0eXBlLCBwYXlsb2FkKSB7XG4gICAgICBleHBlY3QodHlwZSkudG9CZSgncnBjOm1ldGhvZC1jbGllbnQnKTtcbiAgICAgIGV4cGVjdChwYXlsb2FkLm1ldGhvZCkudG9CZSgndGVzdCcpO1xuICAgICAgZXhwZWN0KHBheWxvYWQuc3RhdHVzKS50b0JlKCdmYWlsdXJlJyk7XG4gICAgICBleHBlY3QodHlwZW9mIHBheWxvYWQudGltaW5nKS50b0JlKCdudW1iZXInKTtcbiAgICAgIGV4cGVjdChwYXlsb2FkLmVycm9yKS50b0JlKCdmYWlsdXJlIGRhdGEnKTtcbiAgICB9LFxuICB9KTtcblxuICBjb25zdCBhcHAgPSBjcmVhdGVUZXN0Rml4dHVyZSgpO1xuICAvLyBAdHMtaWdub3JlXG4gIGFwcC5yZWdpc3RlcihGZXRjaFRva2VuLCBtb2NrRmV0Y2hBc0ZhaWx1cmUpO1xuICAvLyAkRmxvd0ZpeE1lXG4gIGFwcC5yZWdpc3RlcihVbml2ZXJzYWxFdmVudHNUb2tlbiwgbW9ja0VtaXR0ZXIpO1xuXG4gIGxldCB3YXNSZXNvbHZlZCA9IGZhbHNlO1xuICBnZXRTaW11bGF0b3IoXG4gICAgYXBwLFxuICAgIGNyZWF0ZVBsdWdpbih7XG4gICAgICBkZXBzOiB7cnBjRmFjdG9yeTogTW9ja1BsdWdpblRva2VufSxcbiAgICAgIHByb3ZpZGVzOiAoZGVwcykgPT4ge1xuICAgICAgICBjb25zdCBycGMgPSBkZXBzLnJwY0ZhY3RvcnkuZnJvbSh7XG4gICAgICAgICAgbWVtb2l6ZWQ6IG5ldyBNYXAoKSxcbiAgICAgICAgfSk7XG4gICAgICAgIGV4cGVjdCh0eXBlb2YgcnBjLnJlcXVlc3QpLnRvQmUoJ2Z1bmN0aW9uJyk7XG4gICAgICAgIGNvbnN0IHRlc3RSZXF1ZXN0ID0gcnBjLnJlcXVlc3QoJ3Rlc3QnKTtcbiAgICAgICAgZXhwZWN0KHRlc3RSZXF1ZXN0IGluc3RhbmNlb2YgUHJvbWlzZSkudG9CZVRydXRoeSgpO1xuICAgICAgICB0ZXN0UmVxdWVzdFxuICAgICAgICAgIC50aGVuKCgpID0+IHtcbiAgICAgICAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgICAgICAgIGRvbmUuZmFpbCgoKSA9PiBuZXcgRXJyb3IoJ3Nob3VsZCByZWplY3QgcHJvbWlzZScpKTtcbiAgICAgICAgICB9KVxuICAgICAgICAgIC5jYXRjaCgoZSkgPT4ge1xuICAgICAgICAgICAgZXhwZWN0KGUpLnRvQmUoJ2ZhaWx1cmUgZGF0YScpO1xuICAgICAgICAgICAgZG9uZSgpO1xuICAgICAgICAgIH0pO1xuXG4gICAgICAgIHdhc1Jlc29sdmVkID0gdHJ1ZTtcbiAgICAgIH0sXG4gICAgfSlcbiAgKTtcblxuICBleHBlY3Qod2FzUmVzb2x2ZWQpLnRvQmVUcnV0aHkoKTtcbn0pO1xuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUEsT0FBT0EsV0FBVyxNQUFNLFFBQVE7QUFFaEMsT0FBT0MsR0FBRyxJQUFHQyxZQUFZLEVBQUVDLFdBQVcsUUFBTyxhQUFhO0FBQzFELFNBQVFDLFVBQVUsUUFBTyxlQUFlO0FBQ3hDLFNBQVFDLFlBQVksUUFBTyxtQkFBbUI7QUFFOUMsU0FBUUMsb0JBQW9CLFFBQU8sZ0NBQWdDO0FBQ25FLFNBQVFDLFNBQVMsUUFBTyxvQkFBb0I7QUFFNUMsT0FBT0MsU0FBUyxNQUFNLFlBQVk7QUFFbEMsT0FBT0MsaUJBQWlCLE1BQU0sdUJBQXVCO0FBQ3JELFNBQVFDLHNCQUFzQixFQUFFQyxtQkFBbUIsUUFBTyxXQUFXO0FBRXJFLE1BQU1DLGVBQTJCLEdBQUdULFdBQVcsQ0FBQyxtQkFBbUIs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