@mjcctech/meteor-desktop
Version:
Build a Meteor's desktop client with hot code push.
224 lines (170 loc) • 29.3 kB
JavaScript
/* eslint-disable global-require */
;
var _fs = _interopRequireDefault(require("fs"));
var _path = _interopRequireDefault(require("path"));
var _assignIn = _interopRequireDefault(require("lodash/assignIn"));
var _commander = _interopRequireDefault(require("commander"));
var _shelljs = _interopRequireDefault(require("shelljs"));
var _ = _interopRequireDefault(require("../.."));
var _addScript = _interopRequireDefault(require("../scripts/utils/addScript"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const {
join
} = _path.default;
const cmd = process.argv[2];
/* eslint-disable no-console */
const {
log,
error,
info,
warn
} = console;
/* eslint-enable no-console */
/**
* Looks for .meteor directory.
* @param {string} appPath - Meteor app path
*/
function isMeteorApp(appPath) {
const meteorPath = join(appPath, '.meteor');
try {
return _fs.default.statSync(meteorPath).isDirectory();
} catch (e) {
return false;
}
}
/**
* Just ensures a ddp url is set.
*
* @param {string|null} ddpUrl - the url that Meteor app connects to
* @returns {string|null}
*/
function getDdpUrl(ddpUrl = null) {
if (!ddpUrl && _commander.default.buildMeteor) {
info('no ddp_url specified, setting default: http://127.0.0.1:3000');
return 'http://127.0.0.1:3000';
}
return ddpUrl;
} // --------------------------
function collect(val, memo) {
memo.push(val);
return memo;
}
_commander.default.option('-b, --build-meteor', 'runs meteor to obtain the mobile build, kills it after').option('-t, --build-timeout <timeout_in_sec>', 'timeout value when waiting for ' + 'meteor to build, default 600sec').option('-p, --port <port>', 'port on which meteor is running, when with -b this will be passed to meteor when obtaining the build').option('--production', 'builds meteor app with the production switch, uglifies contents ' + 'of .desktop, packs app to app.asar').option('-a, --android', 'force adding android as a mobile platform instead of ios').option('-s, --scaffold', 'will scaffold .desktop if not present').option('-i, --ignore-stderr [string]', 'only with -b, strings that when found will not terminate meteor build', collect, []).option('--meteor-settings <path>', 'only with -b, adds --settings options to meteor').option('--prod-debug', 'forces adding dev tools to a production build').option('--ia32', 'generate 32bit installer/package').option('--all-archs', 'generate 32bit and 64bit installers').option('--win', 'generate Windows installer').option('--linux', 'generate Linux installer').option('--mac', 'generate Mac installer').option('-d, --debug', 'run electron with debug switch');
_commander.default.usage('[command] [options]').version(require('./../../package.json').version, '-V, --version').on('--help', () => {
log(' [ddp_url] - pass a ddp url if you want to use different one than used in meteor\'s --mobile-server');
log(' this will also work with -b');
log(' ');
log(' Examples:');
log('');
log(' ', ['# cd into meteor dir first', 'cd /your/meteor/app', 'meteor --mobile-server=127.0.0.1:3000', '', '# open new terminal, assuming you have done npm install --save-dev meteor-desktop', 'npm run desktop -- init', 'npm run desktop'].join('\n '));
log('\n');
});
function verifyArgsSyntax() {
if (process.env.npm_config_argv) {
let npmArgv;
try {
const args = ['-b', '--build-meteor', '-t', '--build-timeout', '-p', '--port', '--production', '-a', '--android', '-s', '--scaffold', '--ia32', '--win', '--linux', '--all-archs', '--win', '--mac', '--meteor-settings'];
npmArgv = JSON.parse(process.env.npm_config_argv);
if (npmArgv.remain.length === 0 && npmArgv.original.length > 2) {
if (npmArgv.original.some(arg => !!~args.indexOf(arg))) {
warn('WARNING: seems that you might used the wrong console syntax, no ` --' + ' ` delimiter was found, be sure you are invoking meteor-desktop with' + ' it when passing commands or options -> ' + '`npm run desktop -- command --option`\n');
}
}
} catch (e) {// Not sure if `npm_config_argv` is always present...
}
}
}
function meteorDesktopFactory(ddpUrl, production = false) {
info(`METEOR-DESKTOP v${require('./../../package.json').version}\n`);
verifyArgsSyntax();
const input = process.cwd();
if (!isMeteorApp(input)) {
error(`not in a meteor app dir\n ${input}`);
process.exit();
}
if (!_commander.default.output) {
_commander.default.output = input;
}
if (production && !_commander.default.production) {
info('package/build-installer implies setting --production, setting it for you');
}
if (!_commander.default.buildMeteor) {
_commander.default.port = _commander.default.port || 3000;
info(`REMINDER: your Meteor project should be running now on port ${_commander.default.port}\n`);
}
if (_commander.default.prodDebug) {
info('!! WARNING: You are adding devTools to a production build !!\n');
}
const options = {
ddpUrl,
skipMobileBuild: _commander.default.buildMeteor ? !_commander.default.buildMeteor : true,
production: _commander.default.production || production
};
(0, _assignIn.default)(options, _commander.default);
return (0, _.default)(input, _commander.default.output, options);
}
function run(ddpUrl) {
meteorDesktopFactory(getDdpUrl(ddpUrl)).run();
}
function build(ddpUrl) {
meteorDesktopFactory(getDdpUrl(ddpUrl)).build();
}
function init() {
meteorDesktopFactory().init();
}
function justRun() {
meteorDesktopFactory().justRun();
}
function runPackager(ddpUrl) {
meteorDesktopFactory(getDdpUrl(ddpUrl), true).runPackager();
}
function buildInstaller(ddpUrl) {
meteorDesktopFactory(getDdpUrl(ddpUrl), true).buildInstaller();
}
function initTestsSupport() {
log('installing cross-env, ava, meteor-desktop-test-suite and spectron');
log('running `meteor npm install --save-dev cross-env ava spectron meteor-desktop-test-suite`');
const {
code
} = _shelljs.default.exec('meteor npm install --save-dev cross-env ava spectron meteor-desktop-test-suite');
if (code !== 0) {
warn('could not add cross-env, ava and spectron to your `devDependencies`, please do it' + ' manually');
}
const test = 'cross-env NODE_ENV=test ava .desktop/**/*.test.js -s --verbose';
const testWatch = 'cross-env NODE_ENV=test ava .desktop/**/*.test.js -s --verbose' + ' --watch --source .desktop';
function fail() {
error('\ncould not add entries to `scripts` in package.json');
log('please try to add it manually\n');
log(`test-desktop: ${test}`);
log(`test-desktop-watch: ${testWatch}`);
}
const packageJsonPath = _path.default.resolve(_path.default.join(process.cwd(), 'package.json'));
(0, _addScript.default)('test-desktop', test, packageJsonPath, fail);
(0, _addScript.default)('test-desktop-watch', testWatch, packageJsonPath, fail);
log('\nadded test-desktop and test-desktop-watch entries');
log('run the test with `npm run test-desktop`');
}
_commander.default.command('init').description('scaffolds .desktop dir in the meteor app').action(init);
_commander.default.command('run [ddp_url]').description('(default) builds and runs desktop app').action(run);
_commander.default.command('build [ddp_url]').description('builds your desktop app').action(build);
_commander.default.command('build-installer [ddp_url]').description('creates the installer').action(buildInstaller);
_commander.default.command('just-run').description('alias for running `electron .` in `.meteor/desktop-build`').action(justRun);
_commander.default.command('package [ddp_url]').description('runs electron packager').action(runPackager);
_commander.default.command('init-tests-support').description('prepares project for running functional tests of desktop app').action(initTestsSupport);
if (process.argv.length === 2 || !~'-h|--help|run|init|build|build-installer|just-run|init-tests-support|package'.indexOf(cmd)) {
let {
argv
} = process;
if (process.argv.length === 2) {
argv.push('run');
} else {
let command = argv.splice(0, 2);
command = command.concat('run', argv);
argv = command;
}
_commander.default.parse(argv);
} else {
_commander.default.parse(process.argv);
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL2xpYi9iaW4vY2xpLmpzIl0sIm5hbWVzIjpbImpvaW4iLCJwYXRoIiwiY21kIiwicHJvY2VzcyIsImFyZ3YiLCJsb2ciLCJlcnJvciIsImluZm8iLCJ3YXJuIiwiY29uc29sZSIsImlzTWV0ZW9yQXBwIiwiYXBwUGF0aCIsIm1ldGVvclBhdGgiLCJmcyIsInN0YXRTeW5jIiwiaXNEaXJlY3RvcnkiLCJlIiwiZ2V0RGRwVXJsIiwiZGRwVXJsIiwicHJvZ3JhbSIsImJ1aWxkTWV0ZW9yIiwiY29sbGVjdCIsInZhbCIsIm1lbW8iLCJwdXNoIiwib3B0aW9uIiwidXNhZ2UiLCJ2ZXJzaW9uIiwicmVxdWlyZSIsIm9uIiwidmVyaWZ5QXJnc1N5bnRheCIsImVudiIsIm5wbV9jb25maWdfYXJndiIsIm5wbUFyZ3YiLCJhcmdzIiwiSlNPTiIsInBhcnNlIiwicmVtYWluIiwibGVuZ3RoIiwib3JpZ2luYWwiLCJzb21lIiwiYXJnIiwiaW5kZXhPZiIsIm1ldGVvckRlc2t0b3BGYWN0b3J5IiwicHJvZHVjdGlvbiIsImlucHV0IiwiY3dkIiwiZXhpdCIsIm91dHB1dCIsInBvcnQiLCJwcm9kRGVidWciLCJvcHRpb25zIiwic2tpcE1vYmlsZUJ1aWxkIiwicnVuIiwiYnVpbGQiLCJpbml0IiwianVzdFJ1biIsInJ1blBhY2thZ2VyIiwiYnVpbGRJbnN0YWxsZXIiLCJpbml0VGVzdHNTdXBwb3J0IiwiY29kZSIsInNoZWxsIiwiZXhlYyIsInRlc3QiLCJ0ZXN0V2F0Y2giLCJmYWlsIiwicGFja2FnZUpzb25QYXRoIiwicmVzb2x2ZSIsImNvbW1hbmQiLCJkZXNjcmlwdGlvbiIsImFjdGlvbiIsInNwbGljZSIsImNvbmNhdCJdLCJtYXBwaW5ncyI6IkFBQUE7O0FBQ0E7OztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUVBOztBQUNBOzs7O0FBRUEsTUFBTTtBQUFFQSxFQUFBQTtBQUFGLElBQVdDLGFBQWpCO0FBQ0EsTUFBTUMsR0FBRyxHQUFHQyxP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QTZCLENBQUMsQ0FBRSwrRUFBK0VJLE9BQS9FLENBQXVGeEMsR0FBdkYsQ0FBcEMsRUFDRTtBQUNFLE1BQUk7QUFBRUUsSUFBQUE7QUFBRixNQUFXRCxPQUFmOztBQUNBLE1BQUlBLE9BQU8sQ0FBQ0MsSUFBUixDQUFha0MsTUFBYixLQUF3QixDQUE1QixFQUErQjtBQUMzQmxDLElBQUFBLElBQUksQ0FBQ29CLElBQUwsQ0FBVSxLQUFWO0FBQ0gsR0FGRCxNQUVPO0FBQ0gsUUFBSTRDLE9BQU8sR0FBR2hFLElBQUksQ0FBQ21FLE1BQUwsQ0FBWSxDQUFaLEVBQWUsQ0FBZixDQUFkO0FBQ0FILElBQUFBLE9BQU8sR0FBR0EsT0FBTyxDQUFDSSxNQUFSLENBQWUsS0FBZixFQUFzQnBFLElBQXRCLENBQVY7QUFDQUEsSUFBQUEsSUFBSSxHQUFHZ0UsT0FBUDtBQUNIOztBQUNEakQscUJBQVFpQixLQUFSLENBQWNoQyxJQUFkO0FBQ0gsQ0FYRCxNQVdPO0FBQ0hlLHFCQUFRaUIsS0FBUixDQUFjakMsT0FBTyxDQUFDQyxJQUF0QjtBQUNIIiwic291cmNlc0NvbnRlbnQiOlsiIyEvdXNyL2Jpbi9lbnYgbm9kZVxuLyogZXNsaW50LWRpc2FibGUgZ2xvYmFsLXJlcXVpcmUgKi9cbmltcG9ydCBmcyBmcm9tICdmcyc7XG5pbXBvcnQgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCBhc3NpZ25JbiBmcm9tICdsb2Rhc2gvYXNzaWduSW4nO1xuaW1wb3J0IHByb2dyYW0gZnJvbSAnY29tbWFuZGVyJztcbmltcG9ydCBzaGVsbCBmcm9tICdzaGVsbGpzJztcblxuaW1wb3J0IG1ldGVvckRlc2t0b3AgZnJvbSAnLi4vLi4nO1xuaW1wb3J0IGFkZFNjcmlwdCBmcm9tICcuLi9zY3JpcHRzL3V0aWxzL2FkZFNjcmlwdCc7XG5cbmNvbnN0IHsgam9pbiB9ID0gcGF0aDtcbmNvbnN0IGNtZCA9IHByb2Nlc3MuYXJndlsyXTtcblxuLyogZXNsaW50LWRpc2FibGUgbm8tY29uc29sZSAqL1xuY29uc3Qge1xuICAgIGxvZywgZXJyb3IsIGluZm8sIHdhcm5cbn0gPSBjb25zb2xlO1xuXG4vKiBlc2xpbnQtZW5hYmxlIG5vLWNvbnNvbGUgKi9cblxuLyoqXG4gKiBMb29rcyBmb3IgLm1ldGVvciBkaXJlY3RvcnkuXG4gKiBAcGFyYW0ge3N0cmluZ30gYXBwUGF0aCAtIE1ldGVvciBhcHAgcGF0aFxuICovXG5mdW5jdGlvbiBpc01ldGVvckFwcChhcHBQYXRoKSB7XG4gICAgY29uc3QgbWV0ZW9yUGF0aCA9IGpvaW4oYXBwUGF0aCwgJy5tZXRlb3InKTtcbiAgICB0cnkge1xuICAgICAgICByZXR1cm4gZnMuc3RhdFN5bmMobWV0ZW9yUGF0aCkuaXNEaXJlY3RvcnkoKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG59XG5cbi8qKlxuICogSnVzdCBlbnN1cmVzIGEgZGRwIHVybCBpcyBzZXQuXG4gKlxuICogQHBhcmFtIHtzdHJpbmd8bnVsbH0gZGRwVXJsIC0gdGhlIHVybCB0aGF0IE1ldGVvciBhcHAgY29ubmVjdHMgdG9cbiAqIEByZXR1cm5zIHtzdHJpbmd8bnVsbH1cbiAqL1xuZnVuY3Rpb24gZ2V0RGRwVXJsKGRkcFVybCA9IG51bGwpIHtcbiAgICBpZiAoIWRkcFVybCAmJiBwcm9ncmFtLmJ1aWxkTWV0ZW9yKSB7XG4gICAgICAgIGluZm8oJ25vIGRkcF91cmwgc3BlY2lmaWVkLCBzZXR0aW5nIGRlZmF1bHQ6IGh0dHA6Ly8xMjcuMC4wLjE6MzAwMCcpO1xuICAgICAgICByZXR1cm4gJ2h0dHA6Ly8xMjcuMC4wLjE6MzAwMCc7XG4gICAgfVxuICAgIHJldHVybiBkZHBVcmw7XG59XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbmZ1bmN0aW9uIGNvbGxlY3QodmFsLCBtZW1vKSB7XG4gICAgbWVtby5wdXNoKHZhbCk7XG4gICAgcmV0dXJuIG1lbW87XG59XG5cbnByb2dyYW1cbiAgICAub3B0aW9uKCctYiwgLS1idWlsZC1tZXRlb3InLCAncnVucyBtZXRlb3IgdG8gb2J0YWluIHRoZSBtb2JpbGUgYnVpbGQsIGtpbGxzIGl0IGFmdGVyJylcbiAgICAub3B0aW9uKCctdCwgLS1idWlsZC10aW1lb3V0IDx0aW1lb3V0X2luX3NlYz4nLCAndGltZW91dCB2YWx1ZSB3aGVuIHdhaXRpbmcgZm9yICcgK1xuICAgICAgICAnbWV0ZW9yIHRvIGJ1aWxkLCBkZWZhdWx0IDYwMHNlYycpXG4gICAgLm9wdGlvbignLXAsIC0tcG9ydCA8cG9ydD4nLCAncG9ydCBvbiB3aGljaCBtZXRlb3IgaXMgcnVubmluZywgd2hlbiB3aXRoIC1iIHRoaXMgd2lsbCBiZSBwYXNzZWQgdG8gbWV0ZW9yIHdoZW4gb2J0YWluaW5nIHRoZSBidWlsZCcpXG4gICAgLm9wdGlvbignLS1wcm9kdWN0aW9uJywgJ2J1aWxkcyBtZXRlb3IgYXBwIHdpdGggdGhlIHByb2R1Y3Rpb24gc3dpdGNoLCB1Z2xpZmllcyBjb250ZW50cyAnICtcbiAgICAgICAgJ29mIC5kZXNrdG9wLCBwYWNrcyBhcHAgdG8gYXBwLmFzYXInKVxuICAgIC5vcHRpb24oJy1hLCAtLWFuZHJvaWQnLCAnZm9yY2UgYWRkaW5nIGFuZHJvaWQgYXMgYSBtb2JpbGUgcGxhdGZvcm0gaW5zdGVhZCBvZiBpb3MnKVxuICAgIC5vcHRpb24oJy1zLCAtLXNjYWZmb2xkJywgJ3dpbGwgc2NhZmZvbGQgLmRlc2t0b3AgaWYgbm90IHByZXNlbnQnKVxuICAgIC5vcHRpb24oJy1pLCAtLWlnbm9yZS1zdGRlcnIgW3N0cmluZ10nLCAnb25seSB3aXRoIC1iLCBzdHJpbmdzIHRoYXQgd2hlbiBmb3VuZCB3aWxsIG5vdCB0ZXJtaW5hdGUgbWV0ZW9yIGJ1aWxkJywgY29sbGVjdCwgW10pXG4gICAgLm9wdGlvbignLS1tZXRlb3Itc2V0dGluZ3MgPHBhdGg+JywgJ29ubHkgd2l0aCAtYiwgYWRkcyAtLXNldHRpbmdzIG9wdGlvbnMgdG8gbWV0ZW9yJylcbiAgICAub3B0aW9uKCctLXByb2QtZGVidWcnLCAnZm9yY2VzIGFkZGluZyBkZXYgdG9vbHMgdG8gYSBwcm9kdWN0aW9uIGJ1aWxkJylcbiAgICAub3B0aW9uKCctLWlhMzInLCAnZ2VuZXJhdGUgMzJiaXQgaW5zdGFsbGVyL3BhY2thZ2UnKVxuICAgIC5vcHRpb24oJy0tYWxsLWFyY2hzJywgJ2dlbmVyYXRlIDMyYml0IGFuZCA2NGJpdCBpbnN0YWxsZXJzJylcbiAgICAub3B0aW9uKCctLXdpbicsICdnZW5lcmF0ZSBXaW5kb3dzIGluc3RhbGxlcicpXG4gICAgLm9wdGlvbignLS1saW51eCcsICdnZW5lcmF0ZSBMaW51eCBpbnN0YWxsZXInKVxuICAgIC5vcHRpb24oJy0tbWFjJywgJ2dlbmVyYXRlIE1hYyBpbnN0YWxsZXInKVxuICAgIC5vcHRpb24oJy1kLCAtLWRlYnVnJywgJ3J1biBlbGVjdHJvbiB3aXRoIGRlYnVnIHN3aXRjaCcpO1xuXG5cbnByb2dyYW1cbiAgICAudXNhZ2UoJ1tjb21tYW5kXSBbb3B0aW9uc10nKVxuICAgIC52ZXJzaW9uKHJlcXVpcmUoJy4vLi4vLi4vcGFja2FnZS5qc29uJykudmVyc2lvbiwgJy1WLCAtLXZlcnNpb24nKVxuICAgIC5vbignLS1oZWxwJywgKCkgPT4ge1xuICAgICAgICBsb2coJyAgW2RkcF91cmxdIC0gcGFzcyBhIGRkcCB1cmwgaWYgeW91IHdhbnQgdG8gdXNlIGRpZmZlcmVudCBvbmUgdGhhbiB1c2VkIGluIG1ldGVvclxcJ3MgLS1tb2JpbGUtc2VydmVyJyk7XG4gICAgICAgIGxvZygnICAgICAgICAgICAgICB0aGlzIHdpbGwgYWxzbyB3b3JrIHdpdGggLWInKTtcbiAgICAgICAgbG9nKCcgICAgJyk7XG4gICAgICAgIGxvZygnICBFeGFtcGxlczonKTtcbiAgICAgICAgbG9nKCcnKTtcbiAgICAgICAgbG9nKFxuICAgICAgICAgICAgJyAgICcsXG4gICAgICAgICAgICBbXG4gICAgICAgICAgICAgICAgJyMgY2QgaW50byBtZXRlb3IgZGlyIGZpcnN0JyxcbiAgICAgICAgICAgICAgICAnY2QgL3lvdXIvbWV0ZW9yL2FwcCcsXG4gICAgICAgICAgICAgICAgJ21ldGVvciAtLW1vYmlsZS1zZXJ2ZXI9MTI3LjAuMC4xOjMwMDAnLFxuICAgICAgICAgICAgICAgICcnLFxuICAgICAgICAgICAgICAgICcjIG9wZW4gbmV3IHRlcm1pbmFsLCBhc3N1bWluZyB5b3UgaGF2ZSBkb25lIG5wbSBpbnN0YWxsIC0tc2F2ZS1kZXYgbWV0ZW9yLWRlc2t0b3AnLFxuICAgICAgICAgICAgICAgICducG0gcnVuIGRlc2t0b3AgLS0gaW5pdCcsXG4gICAgICAgICAgICAgICAgJ25wbSBydW4gZGVza3RvcCdcbiAgICAgICAgICAgIF0uam9pbignXFxuICAgICcpXG4gICAgICAgICk7XG4gICAgICAgIGxvZygnXFxuJyk7XG4gICAgfSk7XG5cblxuZnVuY3Rpb24gdmVyaWZ5QXJnc1N5bnRheCgpIHtcbiAgICBpZiAocHJvY2Vzcy5lbnYubnBtX2NvbmZpZ19hcmd2KSB7XG4gICAgICAgIGxldCBucG1Bcmd2O1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgYXJncyA9IFsnLWInLCAnLS1idWlsZC1tZXRlb3InLCAnLXQnLCAnLS1idWlsZC10aW1lb3V0JywgJy1wJywgJy0tcG9ydCcsXG4gICAgICAgICAgICAgICAgJy0tcHJvZHVjdGlvbicsICctYScsICctLWFuZHJvaWQnLCAnLXMnLCAnLS1zY2FmZm9sZCcsICctLWlhMzInLCAnLS13aW4nLFxuICAgICAgICAgICAgICAgICctLWxpbnV4JywgJy0tYWxsLWFyY2hzJywgJy0td2luJywgJy0tbWFjJywgJy0tbWV0ZW9yLXNldHRpbmdzJ107XG4gICAgICAgICAgICBucG1Bcmd2ID0gSlNPTi5wYXJzZShwcm9jZXNzLmVudi5ucG1fY29uZmlnX2FyZ3YpO1xuICAgICAgICAgICAgaWYgKG5wbUFyZ3YucmVtYWluLmxlbmd0aCA9PT0gMCAmJiBucG1Bcmd2Lm9yaWdpbmFsLmxlbmd0aCA+IDIpIHtcbiAgICAgICAgICAgICAgICBpZiAobnBtQXJndi5vcmlnaW5hbC5zb21lKGFyZyA9PiAhIX5hcmdzLmluZGV4T2YoYXJnKSkpIHtcbiAgICAgICAgICAgICAgICAgICAgd2FybignV0FSTklORzogc2VlbXMgdGhhdCB5b3UgbWlnaHQgdXNlZCB0aGUgd3JvbmcgY29uc29sZSBzeW50YXgsIG5vIGAgLS0nICtcbiAgICAgICAgICAgICAgICAgICAgICAgICcgYCBkZWxpbWl0ZXIgd2FzIGZvdW5kLCBiZSBzdXJlIHlvdSBhcmUgaW52b2tpbmcgbWV0ZW9yLWRlc2t0b3Agd2l0aCcgK1xuICAgICAgICAgICAgICAgICAgICAgICAgJyBpdCB3aGVuIHBhc3NpbmcgY29tbWFuZHMgb3Igb3B0aW9ucyAtPiAnICtcbiAgICAgICAgICAgICAgICAgICAgICAgICdgbnBtIHJ1biBkZXNrdG9wIC0tIGNvbW1hbmQgLS1vcHRpb25gXFxuJyk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAvLyBOb3Qgc3VyZSBpZiBgbnBtX2NvbmZpZ19hcmd2YCBpcyBhbHdheXMgcHJlc2VudC4uLlxuICAgICAgICB9XG4gICAgfVxufVxuXG5mdW5jdGlvbiBtZXRlb3JEZXNrdG9wRmFjdG9yeShkZHBVcmwsIHByb2R1Y3Rpb24gPSBmYWxzZSkge1xuICAgIGluZm8oYE1FVEVPUi1ERVNLVE9QIHYke3JlcXVpcmUoJy4vLi4vLi4vcGFja2FnZS5qc29uJykudmVyc2lvbn1cXG5gKTtcblxuICAgIHZlcmlmeUFyZ3NTeW50YXgoKTtcblxuICAgIGNvbnN0IGlucHV0ID0gcHJvY2Vzcy5jd2QoKTtcblxuICAgIGlmICghaXNNZXRlb3JBcHAoaW5wdXQpKSB7XG4gICAgICAgIGVycm9yKGBub3QgaW4gYSBtZXRlb3IgYXBwIGRpclxcbiAke2lucHV0fWApO1xuICAgICAgICBwcm9jZXNzLmV4aXQoKTtcbiAgICB9XG5cbiAgICBpZiAoIXByb2dyYW0ub3V0cHV0KSB7XG4gICAgICAgIHByb2dyYW0ub3V0cHV0ID0gaW5wdXQ7XG4gICAgfVxuXG4gICAgaWYgKHByb2R1Y3Rpb24gJiYgIXByb2dyYW0ucHJvZHVjdGlvbikge1xuICAgICAgICBpbmZvKCdwYWNrYWdlL2J1aWxkLWluc3RhbGxlciBpbXBsaWVzIHNldHRpbmcgLS1wcm9kdWN0aW9uLCBzZXR0aW5nIGl0IGZvciB5b3UnKTtcbiAgICB9XG5cbiAgICBpZiAoIXByb2dyYW0uYnVpbGRNZXRlb3IpIHtcbiAgICAgICAgcHJvZ3JhbS5wb3J0ID0gcHJvZ3JhbS5wb3J0IHx8IDMwMDA7XG4gICAgICAgIGluZm8oYFJFTUlOREVSOiB5b3VyIE1ldGVvciBwcm9qZWN0IHNob3VsZCBiZSBydW5uaW5nIG5vdyBvbiBwb3J0ICR7cHJvZ3JhbS5wb3J0fVxcbmApO1xuICAgIH1cblxuICAgIGlmIChwcm9ncmFtLnByb2REZWJ1Zykge1xuICAgICAgICBpbmZvKCchISBXQVJOSU5HOiBZb3UgYXJlIGFkZGluZyBkZXZUb29scyB0byBhIHByb2R1Y3Rpb24gYnVpbGQgISFcXG4nKTtcbiAgICB9XG5cbiAgICBjb25zdCBvcHRpb25zID0ge1xuICAgICAgICBkZHBVcmwsXG4gICAgICAgIHNraXBNb2JpbGVCdWlsZDogcHJvZ3JhbS5idWlsZE1ldGVvciA/ICFwcm9ncmFtLmJ1aWxkTWV0ZW9yIDogdHJ1ZSxcbiAgICAgICAgcHJvZHVjdGlvbjogcHJvZ3JhbS5wcm9kdWN0aW9uIHx8IHByb2R1Y3Rpb25cbiAgICB9O1xuXG4gICAgYXNzaWduSW4ob3B0aW9ucywgcHJvZ3JhbSk7XG5cbiAgICByZXR1cm4gbWV0ZW9yRGVza3RvcChcbiAgICAgICAgaW5wdXQsXG4gICAgICAgIHByb2dyYW0ub3V0cHV0LFxuICAgICAgICBvcHRpb25zXG4gICAgKTtcbn1cblxuZnVuY3Rpb24gcnVuKGRkcFVybCkge1xuICAgIG1ldGVvckRlc2t0b3BGYWN0b3J5KGdldERkcFVybChkZHBVcmwpKS5ydW4oKTtcbn1cblxuZnVuY3Rpb24gYnVpbGQoZGRwVXJsKSB7XG4gICAgbWV0ZW9yRGVza3RvcEZhY3RvcnkoZ2V0RGRwVXJsKGRkcFVybCkpLmJ1aWxkKCk7XG59XG5cbmZ1bmN0aW9uIGluaXQoKSB7XG4gICAgbWV0ZW9yRGVza3RvcEZhY3RvcnkoKS5pbml0KCk7XG59XG5cbmZ1bmN0aW9uIGp1c3RSdW4oKSB7XG4gICAgbWV0ZW9yRGVza3RvcEZhY3RvcnkoKS5qdXN0UnVuKCk7XG59XG5cbmZ1bmN0aW9uIHJ1blBhY2thZ2VyKGRkcFVybCkge1xuICAgIG1ldGVvckRlc2t0b3BGYWN0b3J5KGdldERkcFVybChkZHBVcmwpLCB0cnVlKS5ydW5QYWNrYWdlcigpO1xufVxuXG5mdW5jdGlvbiBidWlsZEluc3RhbGxlcihkZHBVcmwpIHtcbiAgICBtZXRlb3JEZXNrdG9wRmFjdG9yeShnZXREZHBVcmwoZGRwVXJsKSwgdHJ1ZSkuYnVpbGRJbnN0YWxsZXIoKTtcbn1cblxuZnVuY3Rpb24gaW5pdFRlc3RzU3VwcG9ydCgpIHtcbiAgICBsb2coJ2luc3RhbGxpbmcgY3Jvc3MtZW52LCBhdmEsIG1ldGVvci1kZXNrdG9wLXRlc3Qtc3VpdGUgYW5kIHNwZWN0cm9uJyk7XG4gICAgbG9nKCdydW5uaW5nIGBtZXRlb3IgbnBtIGluc3RhbGwgLS1zYXZlLWRldiBjcm9zcy1lbnYgYXZhIHNwZWN0cm9uIG1ldGVvci1kZXNrdG9wLXRlc3Qtc3VpdGVgJyk7XG5cbiAgICBjb25zdCB7IGNvZGUgfSA9IHNoZWxsLmV4ZWMoJ21ldGVvciBucG0gaW5zdGFsbCAtLXNhdmUtZGV2IGNyb3NzLWVudiBhdmEgc3BlY3Ryb24gbWV0ZW9yLWRlc2t0b3AtdGVzdC1zdWl0ZScpO1xuXG4gICAgaWYgKGNvZGUgIT09IDApIHtcbiAgICAgICAgd2FybignY291bGQgbm90IGFkZCBjcm9zcy1lbnYsIGF2YSBhbmQgc3BlY3Ryb24gdG8geW91ciBgZGV2RGVwZW5kZW5jaWVzYCwgcGxlYXNlIGRvIGl0JyArXG4gICAgICAgICAgICAnIG1hbnVhbGx5Jyk7XG4gICAgfVxuXG4gICAgY29uc3QgdGVzdCA9ICdjcm9zcy1lbnYgTk9ERV9FTlY9dGVzdCBhdmEgLmRlc2t0b3AvKiovKi50ZXN0LmpzIC1zIC0tdmVyYm9zZSc7XG4gICAgY29uc3QgdGVzdFdhdGNoID0gJ2Nyb3NzLWVudiBOT0RFX0VOVj10ZXN0IGF2YSAuZGVza3RvcC8qKi8qLnRlc3QuanMgLXMgLS12ZXJib3NlJyArXG4gICAgICAgICcgLS13YXRjaCAtLXNvdXJjZSAuZGVza3RvcCc7XG5cbiAgICBmdW5jdGlvbiBmYWlsKCkge1xuICAgICAgICBlcnJvcignXFxuY291bGQgbm90IGFkZCBlbnRyaWVzIHRvIGBzY3JpcHRzYCBpbiBwYWNrYWdlLmpzb24nKTtcbiAgICAgICAgbG9nKCdwbGVhc2UgdHJ5IHRvIGFkZCBpdCBtYW51YWxseVxcbicpO1xuICAgICAgICBsb2coYHRlc3QtZGVza3RvcDogJHt0ZXN0fWApO1xuICAgICAgICBsb2coYHRlc3QtZGVza3RvcC13YXRjaDogJHt0ZXN0V2F0Y2h9YCk7XG4gICAgfVxuXG4gICAgY29uc3QgcGFja2FnZUpzb25QYXRoID0gcGF0aC5yZXNvbHZlKFxuICAgICAgICBwYXRoLmpvaW4ocHJvY2Vzcy5jd2QoKSwgJ3BhY2thZ2UuanNvbicpXG4gICAgKTtcblxuICAgIGFkZFNjcmlwdCgndGVzdC1kZXNrdG9wJywgdGVzdCwgcGFja2FnZUpzb25QYXRoLCBmYWlsKTtcbiAgICBhZGRTY3JpcHQoJ3Rlc3QtZGVza3RvcC13YXRjaCcsIHRlc3RXYXRjaCwgcGFja2FnZUpzb25QYXRoLCBmYWlsKTtcblxuICAgIGxvZygnXFxuYWRkZWQgdGVzdC1kZXNrdG9wIGFuZCB0ZXN0LWRlc2t0b3Atd2F0Y2ggZW50cmllcycpO1xuICAgIGxvZygncnVuIHRoZSB0ZXN0IHdpdGggYG5wbSBydW4gdGVzdC1kZXNrdG9wYCcpO1xufVxuXG5wcm9ncmFtXG4gICAgLmNvbW1hbmQoJ2luaXQnKVxuICAgIC5kZXNjcmlwdGlvbignc2NhZmZvbGRzIC5kZXNrdG9wIGRpciBpbiB0aGUgbWV0ZW9yIGFwcCcpXG4gICAgLmFjdGlvbihpbml0KTtcblxucHJvZ3JhbVxuICAgIC5jb21tYW5kKCdydW4gW2RkcF91cmxdJylcbiAgICAuZGVzY3JpcHRpb24oJyhkZWZhdWx0KSBidWlsZHMgYW5kIHJ1bnMgZGVza3RvcCBhcHAnKVxuICAgIC5hY3Rpb24ocnVuKTtcblxucHJvZ3JhbVxuICAgIC5jb21tYW5kKCdidWlsZCBbZGRwX3VybF0nKVxuICAgIC5kZXNjcmlwdGlvbignYnVpbGRzIHlvdXIgZGVza3RvcCBhcHAnKVxuICAgIC5hY3Rpb24oYnVpbGQpO1xuXG5wcm9ncmFtXG4gICAgLmNvbW1hbmQoJ2J1aWxkLWluc3RhbGxlciBbZGRwX3VybF0nKVxuICAgIC5kZXNjcmlwdGlvbignY3JlYXRlcyB0aGUgaW5zdGFsbGVyJylcbiAgICAuYWN0aW9uKGJ1aWxkSW5zdGFsbGVyKTtcblxucHJvZ3JhbVxuICAgIC5jb21tYW5kKCdqdXN0LXJ1bicpXG4gICAgLmRlc2NyaXB0aW9uKCdhbGlhcyBmb3IgcnVubmluZyBgZWxlY3Ryb24gLmAgaW4gYC5tZXRlb3IvZGVza3RvcC1idWlsZGAnKVxuICAgIC5hY3Rpb24oanVzdFJ1bik7XG5cbnByb2dyYW1cbiAgICAuY29tbWFuZCgncGFja2FnZSBbZGRwX3VybF0nKVxuICAgIC5kZXNjcmlwdGlvbigncnVucyBlbGVjdHJvbiBwYWNrYWdlcicpXG4gICAgLmFjdGlvbihydW5QYWNrYWdlcik7XG5cbnByb2dyYW1cbiAgICAuY29tbWFuZCgnaW5pdC10ZXN0cy1zdXBwb3J0JylcbiAgICAuZGVzY3JpcHRpb24oJ3ByZXBhcmVzIHByb2plY3QgZm9yIHJ1bm5pbmcgZnVuY3Rpb25hbCB0ZXN0cyBvZiBkZXNrdG9wIGFwcCcpXG4gICAgLmFjdGlvbihpbml0VGVzdHNTdXBwb3J0KTtcblxuaWYgKHByb2Nlc3MuYXJndi5sZW5ndGggPT09IDIgfHwgIX4oJy1ofC0taGVscHxydW58aW5pdHxidWlsZHxidWlsZC1pbnN0YWxsZXJ8anVzdC1ydW58aW5pdC10ZXN0cy1zdXBwb3J0fHBhY2thZ2UnLmluZGV4T2YoY21kKSlcbikge1xuICAgIGxldCB7IGFyZ3YgfSA9IHByb2Nlc3M7XG4gICAgaWYgKHByb2Nlc3MuYXJndi5sZW5ndGggPT09IDIpIHtcbiAgICAgICAgYXJndi5wdXNoKCdydW4nKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBsZXQgY29tbWFuZCA9IGFyZ3Yuc3BsaWNlKDAsIDIpO1xuICAgICAgICBjb21tYW5kID0gY29tbWFuZC5jb25jYXQoJ3J1bicsIGFyZ3YpO1xuICAgICAgICBhcmd2ID0gY29tbWFuZDtcbiAgICB9XG4gICAgcHJvZ3JhbS5wYXJzZShhcmd2KTtcbn0gZWxzZSB7XG4gICAgcHJvZ3JhbS5wYXJzZShwcm9jZXNzLmFyZ3YpO1xufVxuIl19