UNPKG

@meteor-community/meteor-desktop

Version:
782 lines (751 loc) 117 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _runtime = _interopRequireDefault(require("regenerator-runtime/runtime")); var _fs = _interopRequireDefault(require("fs")); var _crossSpawn = _interopRequireDefault(require("cross-spawn")); var _semver = _interopRequireDefault(require("semver")); var _shelljs = _interopRequireDefault(require("shelljs")); var _path = _interopRequireDefault(require("path")); var _singleLineLog = _interopRequireDefault(require("single-line-log")); var _asar = _interopRequireDefault(require("@electron/asar")); var _nodeFetch = _interopRequireDefault(require("node-fetch")); var _isDesktopInjector = _interopRequireDefault(require("../skeleton/modules/autoupdate/isDesktopInjector")); var _log = _interopRequireDefault(require("./log")); var _meteorManager = _interopRequireDefault(require("./meteorManager")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } // eslint-disable-next-line no-unused-vars const { join } = _path.default; const sll = _singleLineLog.default.stdout; // TODO: refactor all strategy ifs to one place /** * Represents the Meteor app. * @property {MeteorDesktop} $ * @class */ class MeteorApp { /** * @param {MeteorDesktop} $ - context * @constructor */ constructor($) { this.log = new _log.default('meteorApp'); this.$ = $; this.meteorManager = new _meteorManager.default($); this.mobilePlatform = null; this.oldManifest = null; this.injector = new _isDesktopInjector.default(); this.matcher = new RegExp('__meteor_runtime_config__ = JSON.parse\\(decodeURIComponent\\("([^"]*)"\\)\\)'); this.replacer = new RegExp('(__meteor_runtime_config__ = JSON.parse\\(decodeURIComponent\\()"([^"]*)"(\\)\\))'); this.meteorVersion = null; this.indexHTMLstrategy = null; this.indexHTMLStrategies = { INDEX_FROM_CORDOVA_BUILD: 1, INDEX_FROM_RUNNING_SERVER: 2 }; this.deprectatedPackages = ['omega:meteor-desktop-localstorage']; } /** * Remove any deprecated packages from meteor project. * @returns {Promise<void>} */ async removeDeprecatedPackages() { try { if (this.meteorManager.checkPackages(this.deprectatedPackages)) { this.log.info('deprecated meteor plugins found, removing them'); await this.meteorManager.deletePackages(this.deprectatedPackages); } } catch (e) { throw new Error(e); } } /** * Ensures that required packages are added to the Meteor app. */ async ensureDesktopHCPPackages() { const desktopHCPPackages = ['communitypackages:meteor-desktop-watcher', 'communitypackages:meteor-desktop-bundler']; if (this.$.desktop.getSettings().desktopHCP) { this.log.verbose('desktopHCP is enabled, checking for required packages'); const packagesWithVersion = desktopHCPPackages.map(packageName => `${packageName}@${this.$.getVersion()}`); try { await this.meteorManager.ensurePackages(desktopHCPPackages, packagesWithVersion, 'desktopHCP'); } catch (e) { throw new Error(e); } } else { this.log.verbose('desktopHCP is not enabled, removing required packages'); try { if (this.meteorManager.checkPackages(desktopHCPPackages)) { await this.meteorManager.deletePackages(desktopHCPPackages); } } catch (e) { throw new Error(e); } } } /** * Adds entry to .meteor/.gitignore if necessary. */ updateGitIgnore() { this.log.verbose('updating .meteor/.gitignore'); // Lets read the .meteor/.gitignore and filter out blank lines. const gitIgnore = _fs.default.readFileSync(this.$.env.paths.meteorApp.gitIgnore, 'UTF-8').split('\n').filter(ignoredPath => ignoredPath.trim() !== ''); if (!~gitIgnore.indexOf(this.$.env.paths.electronApp.rootName)) { this.log.verbose(`adding ${this.$.env.paths.electronApp.rootName} to .meteor/.gitignore`); gitIgnore.push(this.$.env.paths.electronApp.rootName); _fs.default.writeFileSync(this.$.env.paths.meteorApp.gitIgnore, gitIgnore.join('\n'), 'UTF-8'); } } /** * Reads the Meteor release version used in the app. * @returns {string} */ getMeteorRelease() { let release = _fs.default.readFileSync(this.$.env.paths.meteorApp.release, 'UTF-8').replace(/\r/gm, '').split('\n')[0]; [, release] = release.split('@'); // We do not care if it is beta. if (~release.indexOf('-')) { [release] = release.split('-'); } return release; } /** * Cast Meteor release to semver version. * @returns {string} */ castMeteorReleaseToSemver() { return `${this.getMeteorRelease()}.0.0`.match(/(^\d+\.\d+\.\d+)/gmi)[0]; } /** * Validate meteor version against a versionRange. * @param {string} versionRange - semver version range */ checkMeteorVersion(versionRange) { const release = this.castMeteorReleaseToSemver(); if (!_semver.default.satisfies(release, versionRange)) { if (this.$.env.options.skipMobileBuild) { this.log.error(`wrong meteor version (${release}) in project - only ` + `${versionRange} is supported`); } else { this.log.error(`wrong meteor version (${release}) in project - only ` + `${versionRange} is supported for automatic meteor builds (you can always ` + 'try with `--skip-mobile-build` if you are using meteor >= 1.2.1'); } process.exit(1); } } /** * Decides which strategy to use while trying to get client build out of Meteor project. * @returns {number} */ chooseStrategy() { if (this.$.env.options.forceCordovaBuild) { return this.indexHTMLStrategies.INDEX_FROM_CORDOVA_BUILD; } const release = this.castMeteorReleaseToSemver(); if (_semver.default.satisfies(release, '> 1.3.4')) { return this.indexHTMLStrategies.INDEX_FROM_RUNNING_SERVER; } if (_semver.default.satisfies(release, '1.3.4')) { const explodedVersion = this.getMeteorRelease().split('.'); if (explodedVersion.length >= 4) { if (explodedVersion[3] > 1) { return this.indexHTMLStrategies.INDEX_FROM_RUNNING_SERVER; } return this.indexHTMLStrategies.INDEX_FROM_CORDOVA_BUILD; } } return this.indexHTMLStrategies.INDEX_FROM_CORDOVA_BUILD; } /** * Checks required preconditions. * - Meteor version * - is mobile platform added */ async checkPreconditions() { if (this.$.env.options.skipMobileBuild) { this.checkMeteorVersion('>= 1.2.1'); } else { this.checkMeteorVersion('>= 1.3.3'); this.indexHTMLstrategy = this.chooseStrategy(); if (this.indexHTMLstrategy === this.indexHTMLStrategies.INDEX_FROM_CORDOVA_BUILD) { this.log.debug('meteor version is < 1.3.4.2 so the index.html from cordova-build will' + ' be used'); } else { this.log.debug('meteor version is >= 1.3.4.2 so the index.html will be downloaded ' + 'from __cordova/index.html'); } } if (!this.$.env.options.skipMobileBuild) { const platforms = _fs.default.readFileSync(this.$.env.paths.meteorApp.platforms, 'UTF-8'); if (!~platforms.indexOf('android') && !~platforms.indexOf('ios')) { if (!this.$.env.options.android) { this.mobilePlatform = 'ios'; } else { this.mobilePlatform = 'android'; } this.log.warn(`no mobile target detected - will add '${this.mobilePlatform}' ` + 'just to get a mobile build'); try { await this.addMobilePlatform(this.mobilePlatform); } catch (e) { this.log.error('failed to add a mobile platform - please try to do it manually'); process.exit(1); } } } } /** * Tries to add a mobile platform to meteor project. * @param {string} platform - platform to add * @returns {Promise} */ addMobilePlatform(platform) { return new Promise((resolve, reject) => { this.log.verbose(`adding mobile platform: ${platform}`); (0, _crossSpawn.default)('meteor', ['add-platform', platform], { cwd: this.$.env.paths.meteorApp.root, stdio: this.$.env.stdio }).on('exit', () => { const platforms = _fs.default.readFileSync(this.$.env.paths.meteorApp.platforms, 'UTF-8'); if (!~platforms.indexOf('android') && !~platforms.indexOf('ios')) { reject(); } else { resolve(); } }); }); } /** * Tries to remove a mobile platform from meteor project. * @param {string} platform - platform to remove * @returns {Promise} */ removeMobilePlatform(platform) { if (this.$.env.options.skipRemoveMobilePlatform) { return Promise.resolve(); } return new Promise((resolve, reject) => { this.log.verbose(`removing mobile platform: ${platform}`); (0, _crossSpawn.default)('meteor', ['remove-platform', platform], { cwd: this.$.env.paths.meteorApp.root, stdio: this.$.env.stdio, env: Object.assign({ METEOR_PRETTY_OUTPUT: 0 }, process.env) }).on('exit', () => { const platforms = _fs.default.readFileSync(this.$.env.paths.meteorApp.platforms, 'UTF-8'); if (~platforms.indexOf(platform)) { reject(); } else { resolve(); } }); }); } /** * Just checks for index.html and program.json existence. * @returns {boolean} */ isCordovaBuildReady() { if (this.indexHTMLstrategy === this.indexHTMLStrategies.INDEX_FROM_CORDOVA_BUILD) { return this.$.utils.exists(this.$.env.paths.meteorApp.cordovaBuildIndex) && this.$.utils.exists(this.$.env.paths.meteorApp.cordovaBuildProgramJson) && (!this.oldManifest || this.oldManifest && this.oldManifest !== _fs.default.readFileSync(this.$.env.paths.meteorApp.cordovaBuildProgramJson, 'UTF-8')); } return this.$.utils.exists(this.$.env.paths.meteorApp.webCordovaProgramJson) && (!this.oldManifest || this.oldManifest && this.oldManifest !== _fs.default.readFileSync(this.$.env.paths.meteorApp.webCordovaProgramJson, 'UTF-8')); } /** * Fetches index.html from running project. * @returns {Promise.<*>} */ async acquireIndex() { const port = this.$.env.options.port ? this.$.env.options.port : 3080; this.log.info('acquiring index.html'); const res = await (0, _nodeFetch.default)(`http://127.0.0.1:${port}/__cordova/index.html`); const text = await res.text(); // Simple test if we really download index.html for web.cordova. if (~text.indexOf('src="/cordova.js"')) { return text; } return false; } /** * Fetches mainfest.json from running project. * @returns {Promise.<void>} */ async acquireManifest() { const port = this.$.env.options.port ? this.$.env.options.port : 3080; this.log.info('acquiring manifest.json'); const res = await (0, _nodeFetch.default)(`http://127.0.0.1:${port}/__cordova/manifest.json?meteor_dont_serve_index=true`); const text = await res.text(); return JSON.parse(text); } /** * Tries to get a mobile build from meteor app. * In case of failure leaves a meteor.log. * A lot of stuff is happening here - but the main aim is to get a mobile build from * .meteor/local/cordova-build/www/application and exit as soon as possible. * * @returns {Promise} */ buildMobileTarget() { const programJson = this.indexHTMLstrategy === this.indexHTMLStrategies.INDEX_FROM_CORDOVA_BUILD ? this.$.env.paths.meteorApp.cordovaBuildProgramJson : this.$.env.paths.meteorApp.webCordovaProgramJson; if (this.$.utils.exists(programJson)) { this.oldManifest = _fs.default.readFileSync(programJson, 'UTF-8'); } return new Promise((resolve, reject) => { const self = this; let log = ''; let desiredExit = false; let buildTimeout = null; let errorTimeout = null; let messageTimeout = null; let killTimeout = null; let cordovaCheckInterval = null; let portProblem = false; function windowsKill(pid) { self.log.debug(`killing pid: ${pid}`); _crossSpawn.default.sync('taskkill', ['/pid', pid, '/f', '/t']); // We will look for other process which might have been created outside the // process tree. // Lets list all node.exe processes. const out = _crossSpawn.default.sync('wmic', ['process', 'where', 'caption="node.exe"', 'get', 'commandline,processid']).stdout.toString('utf-8').split('\n'); const args = self.prepareArguments(); // Lets mount regex. const regexV1 = new RegExp(`${args.join('\\s+')}\\s+(\\d+)`, 'gm'); const regexV2 = new RegExp(`"${args.join('"\\s+"')}"\\s+(\\d+)`, 'gm'); // No we will check for those with the matching params. out.forEach(line => { const match = regexV1.exec(line) || regexV2.exec(line) || false; if (match) { self.log.debug(`killing pid: ${match[1]}`); _crossSpawn.default.sync('taskkill', ['/pid', match[1], '/f', '/t']); } regexV1.lastIndex = 0; regexV2.lastIndex = 0; }); } function writeLog() { _fs.default.writeFileSync('meteor.log', log, 'UTF-8'); } function clearTimeoutsAndIntervals() { clearInterval(cordovaCheckInterval); clearTimeout(buildTimeout); clearTimeout(errorTimeout); clearTimeout(messageTimeout); clearTimeout(killTimeout); } const args = this.prepareArguments(); this.log.info(`running "meteor ${args.join(' ')}"... this might take a while`); const env = { METEOR_PRETTY_OUTPUT: 0, METEOR_NO_RELEASE_CHECK: 1 }; if (this.$.env.options.prodDebug) { env.METEOR_DESKOP_PROD_DEBUG = true; } // Lets spawn meteor. const child = (0, _crossSpawn.default)('meteor', args, { env: Object.assign(env, process.env), cwd: this.$.env.paths.meteorApp.root }, { shell: true }); // Kills the currently running meteor command. function kill(signal = 'SIGKILL') { sll(''); child.kill(signal); if (self.$.env.os.isWindows) { windowsKill(child.pid); } } function exit() { killTimeout = setTimeout(() => { clearTimeoutsAndIntervals(); desiredExit = true; kill('SIGTERM'); resolve(); }, 500); } function copyBuild() { self.copyBuild().then(() => { exit(); }).catch(() => { clearTimeoutsAndIntervals(); kill(); writeLog(); reject('copy'); }); } cordovaCheckInterval = setInterval(() => { // Check if we already have cordova-build ready. if (this.isCordovaBuildReady()) { // If so, then exit immediately. if (this.indexHTMLstrategy === this.indexHTMLStrategies.INDEX_FROM_CORDOVA_BUILD) { copyBuild(); } } }, 1000); child.stderr.on('data', chunk => { const line = chunk.toString('UTF-8'); log += `${line}\n`; if (errorTimeout) { clearTimeout(errorTimeout); } // Do not exit if this is the warning for using --production. // Output exceeds -> https://github.com/meteor/meteor/issues/8592 if (!~line.indexOf('--production') && !~line.indexOf('Output exceeds ') && !~line.indexOf('Node#moveTo') && !~line.indexOf('Browserslist') && Array.isArray(self.$.env.options.ignoreStderr) && self.$.env.options.ignoreStderr.every(str => !~line.indexOf(str))) { self.log.warn('STDERR:', line); // We will exit 1s after last error in stderr. errorTimeout = setTimeout(() => { clearTimeoutsAndIntervals(); kill(); writeLog(); reject('error'); }, 1000); } }); child.stdout.on('data', chunk => { const line = chunk.toString('UTF-8'); if (!desiredExit && line.trim().replace(/[\n\r\t\v\f]+/gm, '') !== '') { const linesToDisplay = line.trim().split('\n\r'); // Only display last line from the chunk. const sanitizedLine = linesToDisplay.pop().replace(/[\n\r\t\v\f]+/gm, ''); sll(sanitizedLine); } log += `${line}\n`; if (~line.indexOf('after_platform_add')) { sll(''); this.log.info('done... 10%'); } if (~line.indexOf('Local package version')) { if (messageTimeout) { clearTimeout(messageTimeout); } messageTimeout = setTimeout(() => { sll(''); this.log.info('building in progress...'); }, 1500); } if (~line.indexOf('Preparing Cordova project')) { sll(''); this.log.info('done... 60%'); } if (~line.indexOf('Can\'t listen on port')) { portProblem = true; } if (~line.indexOf('Your application has errors')) { if (errorTimeout) { clearTimeout(errorTimeout); } errorTimeout = setTimeout(() => { clearTimeoutsAndIntervals(); kill(); writeLog(); reject('errorInApp'); }, 1000); } if (~line.indexOf('App running at')) { copyBuild(); } }); // When Meteor exits child.on('exit', () => { sll(''); clearTimeoutsAndIntervals(); if (!desiredExit) { writeLog(); if (portProblem) { reject('port'); } else { reject('exit'); } } }); buildTimeout = setTimeout(() => { kill(); writeLog(); reject('timeout'); }, this.$.env.options.buildTimeout ? this.$.env.options.buildTimeout * 1000 : 600000); }); } /** * Replaces the DDP url that was used originally when Meteor was building the client. * @param {string} indexHtml - path to index.html from the client */ updateDdpUrl(indexHtml) { let content; let runtimeConfig; try { content = _fs.default.readFileSync(indexHtml, 'UTF-8'); } catch (e) { this.log.error(`error loading index.html file: ${e.message}`); process.exit(1); } if (!this.matcher.test(content)) { this.log.error('could not find runtime config in index file'); process.exit(1); } try { const matches = content.match(this.matcher); runtimeConfig = JSON.parse(decodeURIComponent(matches[1])); } catch (e) { this.log.error('could not find runtime config in index file'); process.exit(1); } if (this.$.env.options.ddpUrl.substr(-1, 1) !== '/') { this.$.env.options.ddpUrl += '/'; } runtimeConfig.ROOT_URL = this.$.env.options.ddpUrl; runtimeConfig.DDP_DEFAULT_CONNECTION_URL = this.$.env.options.ddpUrl; content = content.replace(this.replacer, `$1"${encodeURIComponent(JSON.stringify(runtimeConfig))}"$3`); try { _fs.default.writeFileSync(indexHtml, content); } catch (e) { this.log.error(`error writing index.html file: ${e.message}`); process.exit(1); } this.log.info('successfully updated ddp string in the runtime config of a mobile build' + ` to ${this.$.env.options.ddpUrl}`); } /** * Prepares the arguments passed to `meteor` command. * @returns {string[]} */ prepareArguments() { const args = ['run', '--verbose', `--mobile-server=${this.$.env.options.ddpUrl}`]; if (this.$.env.isProductionBuild()) { args.push('--production'); } args.push('-p'); if (this.$.env.options.port) { args.push(this.$.env.options.port); } else { args.push('3080'); } if (this.$.env.options.meteorSettings) { args.push('--settings', this.$.env.options.meteorSettings); } return args; } /** * Validates the mobile build and copies it into electron app. */ async copyBuild() { this.log.debug('clearing build dir'); try { await this.$.utils.rmWithRetries('-rf', this.$.env.paths.electronApp.meteorApp); } catch (e) { throw new Error(e); } let prefix = 'cordovaBuild'; let copyPathPostfix = ''; if (this.indexHTMLstrategy === this.indexHTMLStrategies.INDEX_FROM_RUNNING_SERVER) { prefix = 'webCordova'; copyPathPostfix = `${_path.default.sep}*`; let indexHtml; try { _fs.default.mkdirSync(this.$.env.paths.electronApp.meteorApp); indexHtml = await this.acquireIndex(); _fs.default.writeFileSync(this.$.env.paths.electronApp.meteorAppIndex, indexHtml); this.log.info('successfully downloaded index.html from running meteor app'); } catch (e) { this.log.error('error while trying to download index.html for web.cordova, ' + 'be sure that you are running a mobile target or with' + ' --mobile-server: ', e); throw e; } } const cordovaBuild = this.$.env.paths.meteorApp[prefix]; const { cordovaBuildIndex } = this.$.env.paths.meteorApp; const cordovaBuildProgramJson = this.$.env.paths.meteorApp[`${prefix}ProgramJson`]; if (!this.$.utils.exists(cordovaBuild)) { this.log.error(`no mobile build found at ${cordovaBuild}`); this.log.error('are you sure you did run meteor with --mobile-server?'); throw new Error('required file not present'); } if (!this.$.utils.exists(cordovaBuildProgramJson)) { this.log.error('no program.json found in mobile build found at ' + `${cordovaBuild}`); this.log.error('are you sure you did run meteor with --mobile-server?'); throw new Error('required file not present'); } if (this.indexHTMLstrategy !== this.indexHTMLStrategies.INDEX_FROM_RUNNING_SERVER) { if (!this.$.utils.exists(cordovaBuildIndex)) { this.log.error('no index.html found in cordova build found at ' + `${cordovaBuild}`); this.log.error('are you sure you did run meteor with --mobile-server?'); throw new Error('required file not present'); } } this.log.verbose('copying mobile build'); _shelljs.default.cp('-R', `${cordovaBuild}${copyPathPostfix}`, this.$.env.paths.electronApp.meteorApp); // Because of various permission problems here we try to clear te path by clearing // all possible restrictions. _shelljs.default.chmod('-R', '777', this.$.env.paths.electronApp.meteorApp); if (this.$.env.os.isWindows) { _shelljs.default.exec(`attrib -r ${this.$.env.paths.electronApp.meteorApp}${_path.default.sep}*.* /s`); } if (this.indexHTMLstrategy === this.indexHTMLStrategies.INDEX_FROM_RUNNING_SERVER) { let programJson; try { programJson = await this.acquireManifest(); _fs.default.writeFileSync(this.$.env.paths.electronApp.meteorAppProgramJson, JSON.stringify(programJson, null, 4)); this.log.info('successfully downloaded manifest.json from running meteor app'); } catch (e) { this.log.error('error while trying to download manifest.json for web.cordova,' + ' be sure that you are running a mobile target or with' + ' --mobile-server: ', e); throw e; } } this.log.info('mobile build copied to electron app'); this.log.debug('copy cordova.js to meteor build'); _shelljs.default.cp(join(__dirname, '..', 'skeleton', 'cordova.js'), this.$.env.paths.electronApp.meteorApp); } /** * Injects Meteor.isDesktop */ injectIsDesktop() { this.log.info('injecting isDesktop'); let manifestJsonPath = this.$.env.paths.meteorApp.cordovaBuildProgramJson; if (this.indexHTMLstrategy === this.indexHTMLStrategies.INDEX_FROM_RUNNING_SERVER) { manifestJsonPath = this.$.env.paths.meteorApp.webCordovaProgramJson; } try { const { manifest } = JSON.parse(_fs.default.readFileSync(manifestJsonPath, 'UTF-8')); let injected = false; let injectedStartupDidComplete = false; let result = null; // We will search in every .js file in the manifest. // We could probably detect whether this is a dev or production build and only search in // the correct files, but for now this should be fine. manifest.forEach(file => { let fileContents; // Hacky way of setting isDesktop. if (file.type === 'js') { fileContents = _fs.default.readFileSync(join(this.$.env.paths.electronApp.meteorApp, file.path), 'UTF-8'); result = this.injector.processFileContents(fileContents); ({ fileContents } = result); injectedStartupDidComplete = result.injectedStartupDidComplete ? true : injectedStartupDidComplete; injected = result.injected ? true : injected; _fs.default.writeFileSync(join(this.$.env.paths.electronApp.meteorApp, file.path), fileContents); } }); if (!injected) { this.log.error('error injecting isDesktop global var.'); process.exit(1); } if (!injectedStartupDidComplete) { this.log.error('error injecting isDesktop for startupDidComplete'); process.exit(1); } } catch (e) { this.log.error('error occurred while injecting isDesktop: ', e); process.exit(1); } this.log.info('injected successfully'); } /** * Builds, modifies and copies the meteor app to electron app. */ async build() { this.log.info('checking for any mobile platform'); try { await this.checkPreconditions(); } catch (e) { this.log.error('error occurred during checking preconditions: ', e); process.exit(1); } this.log.info('building meteor app'); if (!this.$.env.options.skipMobileBuild) { try { await this.buildMobileTarget(); } catch (reason) { switch (reason) { case 'timeout': this.log.error('timeout while building, log has been written to meteor.log'); break; case 'error': this.log.error('build was terminated by meteor-desktop as some errors were reported to stderr, you ' + 'should see it above, also check meteor.log for more info, to ignore it use the ' + '--ignore-stderr "<string>"'); break; case 'errorInApp': this.log.error('your meteor app has errors - look into meteor.log for more' + ' info'); break; case 'port': this.log.error('your port 3080 is currently used (you probably have this or other ' + 'meteor project running?), use `-t` or `--meteor-port` to use ' + 'different port while building'); break; case 'exit': this.log.error('meteor cmd exited unexpectedly, log has been written to meteor.log'); break; case 'copy': this.log.error('error encountered when copying the build'); break; default: this.log.error('error occurred during building mobile target', reason); } if (this.mobilePlatform) { await this.removeMobilePlatform(this.mobilePlatform); } process.exit(1); } } else { this.indexHTMLstrategy = this.chooseStrategy(); try { await this.copyBuild(); } catch (e) { process.exit(1); } } this.injectIsDesktop(); this.changeDdpUrl(); try { await this.packToAsar(); } catch (e) { this.log.error('error while packing meteor app to asar'); process.exit(1); } this.log.info('meteor build finished'); if (this.mobilePlatform) { await this.removeMobilePlatform(this.mobilePlatform); } } changeDdpUrl() { if (this.$.env.options.ddpUrl !== null) { try { this.updateDdpUrl(this.$.env.paths.electronApp.meteorAppIndex); } catch (e) { this.log.error(`error while trying to change the ddp url: ${e.message}`); } } } packToAsar() { this.log.info('packing meteor app to asar archive'); return new Promise((resolve, reject) => _asar.default.createPackage(this.$.env.paths.electronApp.meteorApp, _path.default.join(this.$.env.paths.electronApp.root, 'meteor.asar')).then(() => { // On Windows some files might still be blocked. Giving a tick for them to be // ready for deletion. setImmediate(() => { this.log.verbose('clearing meteor app after packing'); this.$.utils.rmWithRetries('-rf', this.$.env.paths.electronApp.meteorApp).then(() => { resolve(); }).catch(e => { reject(e); }); }); })); } /** * Wrapper for spawning npm. * @param {Array} commands - commands for spawn * @param {string} stdio * @param {string} cwd * @return {Promise} */ runNpm(commands, stdio = 'ignore', cwd = this.$.env.paths.meteorApp.root) { return new Promise((resolve, reject) => { this.log.verbose(`executing meteor npm ${commands.join(' ')}`); (0, _crossSpawn.default)('meteor', ['npm', ...commands], { cwd, stdio }).on('exit', code => code === 0 ? resolve() : reject(new Error(`npm exit code was ${code}`))); }); } } exports.default = MeteorApp; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcnVudGltZSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwiX2ZzIiwiX2Nyb3NzU3Bhd24iLCJfc2VtdmVyIiwiX3NoZWxsanMiLCJfcGF0aCIsIl9zaW5nbGVMaW5lTG9nIiwiX2FzYXIiLCJfbm9kZUZldGNoIiwiX2lzRGVza3RvcEluamVjdG9yIiwiX2xvZyIsIl9tZXRlb3JNYW5hZ2VyIiwiZSIsIl9fZXNNb2R1bGUiLCJkZWZhdWx0Iiwiam9pbiIsInBhdGgiLCJzbGwiLCJzaW5nbGVMaW5lTG9nIiwic3Rkb3V0IiwiTWV0ZW9yQXBwIiwiY29uc3RydWN0b3IiLCIkIiwibG9nIiwiTG9nIiwibWV0ZW9yTWFuYWdlciIsIk1ldGVvck1hbmFnZXIiLCJtb2JpbGVQbGF0Zm9ybSIsIm9sZE1hbmlmZXN0IiwiaW5qZWN0b3IiLCJJc0Rlc2t0b3BJbmplY3RvciIsIm1hdGNoZXIiLCJSZWdFeHAiLCJyZXBsYWNlciIsIm1ldGVvclZlcnNpb24iLCJpbmRleEhUTUxzdHJhdGVneSIsImluZGV4SFRNTFN0cmF0ZWdpZXMiLCJJTkRFWF9GUk9NX0NPUkRPVkFfQlVJTEQiLCJJTkRFWF9GUk9NX1JVTk5JTkdfU0VSVkVSIiwiZGVwcmVjdGF0ZWRQYWNrYWdlcyIsInJlbW92ZURlcHJlY2F0ZWRQYWNrYWdlcyIsImNoZWNrUGFja2FnZXMiLCJpbmZvIiwiZGVsZXRlUGFja2FnZXMiLCJFcnJvciIsImVuc3VyZURlc2t0b3BIQ1BQYWNrYWdlcyIsImRlc2t0b3BIQ1BQYWNrYWdlcyIsImRlc2t0b3AiLCJnZXRTZXR0aW5ncyIsImRlc2t0b3BIQ1AiLCJ2ZXJib3NlIiwicGFja2FnZXNXaXRoVmVyc2lvbiIsIm1hcCIsInBhY2thZ2VOYW1lIiwiZ2V0VmVyc2lvbiIsImVuc3VyZVBhY2thZ2VzIiwidXBkYXRlR2l0SWdub3JlIiwiZ2l0SWdub3JlIiwiZnMiLCJyZWFkRmlsZVN5bmMiLCJlbnYiLCJwYXRocyIsIm1ldGVvckFwcCIsInNwbGl0IiwiZmlsdGVyIiwiaWdub3JlZFBhdGgiLCJ0cmltIiwiaW5kZXhPZiIsImVsZWN0cm9uQXBwIiwicm9vdE5hbWUiLCJwdXNoIiwid3JpdGVGaWxlU3luYyIsImdldE1ldGVvclJlbGVhc2UiLCJyZWxlYXNlIiwicmVwbGFjZSIsImNhc3RNZXRlb3JSZWxlYXNlVG9TZW12ZXIiLCJtYXRjaCIsImNoZWNrTWV0ZW9yVmVyc2lvbiIsInZlcnNpb25SYW5nZSIsInNlbXZlciIsInNhdGlzZmllcyIsIm9wdGlvbnMiLCJza2lwTW9iaWxlQnVpbGQiLCJlcnJvciIsInByb2Nlc3MiLCJleGl0IiwiY2hvb3NlU3RyYXRlZ3kiLCJmb3JjZUNvcmRvdmFCdWlsZCIsImV4cGxvZGVkVmVyc2lvbiIsImxlbmd0aCIsImNoZWNrUHJlY29uZGl0aW9ucyIsImRlYnVnIiwicGxhdGZvcm1zIiwiYW5kcm9pZCIsIndhcm4iLCJhZGRNb2JpbGVQbGF0Zm9ybSIsInBsYXRmb3JtIiwiUHJvbWlzZSIsInJlc29sdmUiLCJyZWplY3QiLCJzcGF3biIsImN3ZCIsInJvb3QiLCJzdGRpbyIsIm9uIiwicmVtb3ZlTW9iaWxlUGxhdGZvcm0iLCJza2lwUmVtb3ZlTW9iaWxlUGxhdGZvcm0iLCJPYmplY3QiLCJhc3NpZ24iLCJNRVRFT1JfUFJFVFRZX09VVFBVVCIsImlzQ29yZG92YUJ1aWxkUmVhZHkiLCJ1dGlscyIsImV4aXN0cyIsImNvcmRvdmFCdWlsZEluZGV4IiwiY29yZG92YUJ1aWxkUHJvZ3JhbUpzb24iLCJ3ZWJDb3Jkb3ZhUHJvZ3JhbUpzb24iLCJhY3F1aXJlSW5kZXgiLCJwb3J0IiwicmVzIiwiZmV0Y2giLCJ0ZXh0IiwiYWNxdWlyZU1hbmlmZXN0IiwiSlNPTiIsInBhcnNlIiwiYnVpbGRNb2JpbGVUYXJnZXQiLCJwcm9ncmFtSnNvbiIsInNlbGYiLCJkZXNpcmVkRXhpdCIsImJ1aWxkVGltZW91dCIsImVycm9yVGltZW91dCIsIm1lc3NhZ2VUaW1lb3V0Iiwia2lsbFRpbWVvdXQiLCJjb3Jkb3ZhQ2hlY2tJbnRlcnZhbCIsInBvcnRQcm9ibGVtIiwid2luZG93c0tpbGwiLCJwaWQiLCJzeW5jIiwib3V0IiwidG9TdHJpbmciLCJhcmdzIiwicHJlcGFyZUFyZ3VtZW50cyIsInJlZ2V4VjEiLCJyZWdleFYyIiwiZm9yRWFjaCIsImxpbmUiLCJleGVjIiwibGFzdEluZGV4Iiwid3JpdGVMb2ciLCJjbGVhclRpbWVvdXRzQW5kSW50ZXJ2YWxzIiwiY2xlYXJJbnRlcnZhbCIsImNsZWFyVGltZW91dCIsIk1FVEVPUl9OT19SRUxFQVNFX0NIRUNLIiwicHJvZERlYnVnIiwiTUVURU9SX0RFU0tPUF9QUk9EX0RFQlVHIiwiY2hpbGQiLCJzaGVsbCIsImtpbGwiLCJzaWduYWwiLCJvcyIsImlzV2luZG93cyIsInNldFRpbWVvdXQiLCJjb3B5QnVpbGQiLCJ0aGVuIiwiY2F0Y2giLCJzZXRJbnRlcnZhbCIsInN0ZGVyciIsImNodW5rIiwiQXJyYXkiLCJpc0FycmF5IiwiaWdub3JlU3RkZXJyIiwiZXZlcnkiLCJzdHIiLCJsaW5lc1RvRGlzcGxheSIsInNhbml0aXplZExpbmUiLCJwb3AiLCJ1cGRhdGVEZHBVcmwiLCJpbmRleEh0bWwiLCJjb250ZW50IiwicnVudGltZUNvbmZpZyIsIm1lc3NhZ2UiLCJ0ZXN0IiwibWF0Y2hlcyIsImRlY29kZVVSSUNvbXBvbmVudCIsImRkcFVybCIsInN1YnN0ciIsIlJPT1RfVVJMIiwiRERQX0RFRkFVTFRfQ09OTkVDVElPTl9VUkwiLCJlbmNvZGVVUklDb21wb25lbnQiLCJzdHJpbmdpZnkiLCJpc1Byb2R1Y3Rpb25CdWlsZCIsIm1ldGVvclNldHRpbmdzIiwicm1XaXRoUmV0cmllcyIsInByZWZpeCIsImNvcHlQYXRoUG9zdGZpeCIsInNlcCIsIm1rZGlyU3luYyIsIm1ldGVvckFwcEluZGV4IiwiY29yZG92YUJ1aWxkIiwiY3AiLCJjaG1vZCIsIm1ldGVvckFwcFByb2dyYW1Kc29uIiwiX19kaXJuYW1lIiwiaW5qZWN0SXNEZXNrdG9wIiwibWFuaWZlc3RKc29uUGF0aCIsIm1hbmlmZXN0IiwiaW5qZWN0ZWQiLCJpbmplY3RlZFN0YXJ0dXBEaWRDb21wbGV0ZSIsInJlc3VsdCIsImZpbGUiLCJmaWxlQ29udGVudHMiLCJ0eXBlIiwicHJvY2Vzc0ZpbGVDb250ZW50cyIsImJ1aWxkIiwicmVhc29uIiwiY2hhbmdlRGRwVXJsIiwicGFja1RvQXNhciIsImFzYXIiLCJjcmVhdGVQYWNrYWdlIiwic2V0SW1tZWRpYXRlIiwicnVuTnBtIiwiY29tbWFuZHMiLCJjb2RlIiwiZXhwb3J0cyJdLCJzb3VyY2VzIjpbIi4uL2xpYi9tZXRlb3JBcHAuanMiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVudXNlZC12YXJzXG5pbXBvcnQgcmVnZW5lcmF0b3JSdW50aW1lIGZyb20gJ3JlZ2VuZXJhdG9yLXJ1bnRpbWUvcnVudGltZSc7XG5pbXBvcnQgZnMgZnJvbSAnZnMnO1xuaW1wb3J0IHNwYXduIGZyb20gJ2Nyb3NzLXNwYXduJztcbmltcG9ydCBzZW12ZXIgZnJvbSAnc2VtdmVyJztcbmltcG9ydCBzaGVsbCBmcm9tICdzaGVsbGpzJztcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHNpbmdsZUxpbmVMb2cgZnJvbSAnc2luZ2xlLWxpbmUtbG9nJztcbmltcG9ydCBhc2FyIGZyb20gJ0BlbGVjdHJvbi9hc2FyJztcbmltcG9ydCBmZXRjaCBmcm9tICdub2RlLWZldGNoJztcblxuaW1wb3J0IElzRGVza3RvcEluamVjdG9yIGZyb20gJy4uL3NrZWxldG9uL21vZHVsZXMvYXV0b3VwZGF0ZS9pc0Rlc2t0b3BJbmplY3Rvcic7XG5pbXBvcnQgTG9nIGZyb20gJy4vbG9nJztcbmltcG9ydCBNZXRlb3JNYW5hZ2VyIGZyb20gJy4vbWV0ZW9yTWFuYWdlcic7XG5cbmNvbnN0IHsgam9pbiB9ID0gcGF0aDtcbmNvbnN0IHNsbCA9IHNpbmdsZUxpbmVMb2cuc3Rkb3V0O1xuXG4vLyBUT0RPOiByZWZhY3RvciBhbGwgc3RyYXRlZ3kgaWZzIHRvIG9uZSBwbGFjZVxuXG4vKipcbiAqIFJlcHJlc2VudHMgdGhlIE1ldGVvciBhcHAuXG4gKiBAcHJvcGVydHkge01ldGVvckRlc2t0b3B9ICRcbiAqIEBjbGFzc1xuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBNZXRlb3JBcHAge1xuICAgIC8qKlxuICAgICAqIEBwYXJhbSB7TWV0ZW9yRGVza3RvcH0gJCAtIGNvbnRleHRcbiAgICAgKiBAY29uc3RydWN0b3JcbiAgICAgKi9cbiAgICBjb25zdHJ1Y3RvcigkKSB7XG4gICAgICAgIHRoaXMubG9nID0gbmV3IExvZygnbWV0ZW9yQXBwJyk7XG4gICAgICAgIHRoaXMuJCA9ICQ7XG4gICAgICAgIHRoaXMubWV0ZW9yTWFuYWdlciA9IG5ldyBNZXRlb3JNYW5hZ2VyKCQpO1xuICAgICAgICB0aGlzLm1vYmlsZVBsYXRmb3JtID0gbnVsbDtcbiAgICAgICAgdGhpcy5vbGRNYW5pZmVzdCA9IG51bGw7XG4gICAgICAgIHRoaXMuaW5qZWN0b3IgPSBuZXcgSXNEZXNrdG9wSW5qZWN0b3IoKTtcbiAgICAgICAgdGhpcy5tYXRjaGVyID0gbmV3IFJlZ0V4cChcbiAgICAgICAgICAgICdfX21ldGVvcl9ydW50aW1lX2NvbmZpZ19fID0gSlNPTi5wYXJzZVxcXFwoZGVjb2RlVVJJQ29tcG9uZW50XFxcXChcIihbXlwiXSopXCJcXFxcKVxcXFwpJ1xuICAgICAgICApO1xuICAgICAgICB0aGlzLnJlcGxhY2VyID0gbmV3IFJlZ0V4cChcbiAgICAgICAgICAgICcoX19tZXRlb3JfcnVudGltZV9jb25maWdfXyA9IEpTT04ucGFyc2VcXFxcKGRlY29kZVVSSUNvbXBvbmVudFxcXFwoKVwiKFteXCJdKilcIihcXFxcKVxcXFwpKSdcbiAgICAgICAgKTtcbiAgICAgICAgdGhpcy5tZXRlb3JWZXJzaW9uID0gbnVsbDtcbiAgICAgICAgdGhpcy5pbmRleEhUTUxzdHJhdGVneSA9IG51bGw7XG5cbiAgICAgICAgdGhpcy5pbmRleEhUTUxTdHJhdGVnaWVzID0ge1xuICAgICAgICAgICAgSU5ERVhfRlJPTV9DT1JET1ZBX0JVSUxEOiAxLFxuICAgICAgICAgICAgSU5ERVhfRlJPTV9SVU5OSU5HX1NFUlZFUjogMlxuICAgICAgICB9O1xuXG4gICAgICAgIHRoaXMuZGVwcmVjdGF0ZWRQYWNrYWdlcyA9IFsnb21lZ2E6bWV0ZW9yLWRlc2t0b3AtbG9jYWxzdG9yYWdlJ107XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlIGFueSBkZXByZWNhdGVkIHBhY2thZ2VzIGZyb20gbWV0ZW9yIHByb2plY3QuXG4gICAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gICAgICovXG4gICAgYXN5bmMgcmVtb3ZlRGVwcmVjYXRlZFBhY2thZ2VzKCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgaWYgKHRoaXMubWV0ZW9yTWFuYWdlci5jaGVja1BhY2thZ2VzKHRoaXMuZGVwcmVjdGF0ZWRQYWNrYWdlcykpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmxvZy5pbmZvKCdkZXByZWNhdGVkIG1ldGVvciBwbHVnaW5zIGZvdW5kLCByZW1vdmluZyB0aGVtJyk7XG4gICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5tZXRlb3JNYW5hZ2VyLmRlbGV0ZVBhY2thZ2VzKHRoaXMuZGVwcmVjdGF0ZWRQYWNrYWdlcyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihlKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEVuc3VyZXMgdGhhdCByZXF1aXJlZCBwYWNrYWdlcyBhcmUgYWRkZWQgdG8gdGhlIE1ldGVvciBhcHAuXG4gICAgICovXG4gICAgYXN5bmMgZW5zdXJlRGVza3RvcEhDUFBhY2thZ2VzKCkge1xuICAgICAgICBjb25zdCBkZXNrdG9wSENQUGFja2FnZXMgPSBbJ2NvbW11bml0eXBhY2thZ2VzOm1ldGVvci1kZXNrdG9wLXdhdGNoZXInLCAnY29tbXVuaXR5cGFja2FnZXM6bWV0ZW9yLWRlc2t0b3AtYnVuZGxlciddO1xuICAgICAgICBpZiAodGhpcy4kLmRlc2t0b3AuZ2V0U2V0dGluZ3MoKS5kZXNrdG9wSENQKSB7XG4gICAgICAgICAgICB0aGlzLmxvZy52ZXJib3NlKCdkZXNrdG9wSENQIGlzIGVuYWJsZWQsIGNoZWNraW5nIGZvciByZXF1aXJlZCBwYWNrYWdlcycpO1xuXG4gICAgICAgICAgICBjb25zdCBwYWNrYWdlc1dpdGhWZXJzaW9uID0gZGVza3RvcEhDUFBhY2thZ2VzLm1hcChwYWNrYWdlTmFtZSA9PiBgJHtwYWNrYWdlTmFtZX1AJHt0aGlzLiQuZ2V0VmVyc2lvbigpfWApO1xuXG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIGF3YWl0IHRoaXMubWV0ZW9yTWFuYWdlci5lbnN1cmVQYWNrYWdlcyhkZXNrdG9wSENQUGFja2FnZXMsIHBhY2thZ2VzV2l0aFZlcnNpb24sICdkZXNrdG9wSENQJyk7XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGUpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5sb2cudmVyYm9zZSgnZGVza3RvcEhDUCBpcyBub3QgZW5hYmxlZCwgcmVtb3ZpbmcgcmVxdWlyZWQgcGFja2FnZXMnKTtcblxuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5tZXRlb3JNYW5hZ2VyLmNoZWNrUGFja2FnZXMoZGVza3RvcEhDUFBhY2thZ2VzKSkge1xuICAgICAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLm1ldGVvck1hbmFnZXIuZGVsZXRlUGFja2FnZXMoZGVza3RvcEhDUFBhY2thZ2VzKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGUpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQWRkcyBlbnRyeSB0byAubWV0ZW9yLy5naXRpZ25vcmUgaWYgbmVjZXNzYXJ5LlxuICAgICAqL1xuICAgIHVwZGF0ZUdpdElnbm9yZSgpIHtcbiAgICAgICAgdGhpcy5sb2cudmVyYm9zZSgndXBkYXRpbmcgLm1ldGVvci8uZ2l0aWdub3JlJyk7XG4gICAgICAgIC8vIExldHMgcmVhZCB0aGUgLm1ldGVvci8uZ2l0aWdub3JlIGFuZCBmaWx0ZXIgb3V0IGJsYW5rIGxpbmVzLlxuICAgICAgICBjb25zdCBnaXRJZ25vcmUgPSBmcy5yZWFkRmlsZVN5bmModGhpcy4kLmVudi5wYXRocy5tZXRlb3JBcHAuZ2l0SWdub3JlLCAnVVRGLTgnKVxuICAgICAgICAgICAgLnNwbGl0KCdcXG4nKS5maWx0ZXIoaWdub3JlZFBhdGggPT4gaWdub3JlZFBhdGgudHJpbSgpICE9PSAnJyk7XG5cbiAgICAgICAgaWYgKCF+Z2l0SWdub3JlLmluZGV4T2YodGhpcy4kLmVudi5wYXRocy5lbGVjdHJvbkFwcC5yb290TmFtZSkpIHtcbiAgICAgICAgICAgIHRoaXMubG9nLnZlcmJvc2UoYGFkZGluZyAke3RoaXMuJC5lbnYucGF0aHMuZWxlY3Ryb25BcHAucm9vdE5hbWV9IHRvIC5tZXRlb3IvLmdpdGlnbm9yZWApO1xuICAgICAgICAgICAgZ2l0SWdub3JlLnB1c2godGhpcy4kLmVudi5wYXRocy5lbGVjdHJvbkFwcC5yb290TmFtZSk7XG5cbiAgICAgICAgICAgIGZzLndyaXRlRmlsZVN5bmModGhpcy4kLmVudi5wYXRocy5tZXRlb3JBcHAuZ2l0SWdub3JlLCBnaXRJZ25vcmUuam9pbignXFxuJyksICdVVEYtOCcpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVhZHMgdGhlIE1ldGVvciByZWxlYXNlIHZlcnNpb24gdXNlZCBpbiB0aGUgYXBwLlxuICAgICAqIEByZXR1cm5zIHtzdHJpbmd9XG4gICAgICovXG4gICAgZ2V0TWV0ZW9yUmVsZWFzZSgpIHtcbiAgICAgICAgbGV0IHJlbGVhc2UgPSBmcy5yZWFkRmlsZVN5bmModGhpcy4kLmVudi5wYXRocy5tZXRlb3JBcHAucmVsZWFzZSwgJ1VURi04JylcbiAgICAgICAgICAgIC5yZXBsYWNlKC9cXHIvZ20sICcnKVxuICAgICAgICAgICAgLnNwbGl0KCdcXG4nKVswXTtcbiAgICAgICAgKFssIHJlbGVhc2VdID0gcmVsZWFzZS5zcGxpdCgnQCcpKTtcbiAgICAgICAgLy8gV2UgZG8gbm90IGNhcmUgaWYgaXQgaXMgYmV0YS5cbiAgICAgICAgaWYgKH5yZWxlYXNlLmluZGV4T2YoJy0nKSkge1xuICAgICAgICAgICAgKFtyZWxlYXNlXSA9IHJlbGVhc2Uuc3BsaXQoJy0nKSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHJlbGVhc2U7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2FzdCBNZXRlb3IgcmVsZWFzZSB0byBzZW12ZXIgdmVyc2lvbi5cbiAgICAgKiBAcmV0dXJucyB7c3RyaW5nfVxuICAgICAqL1xuICAgIGNhc3RNZXRlb3JSZWxlYXNlVG9TZW12ZXIoKSB7XG4gICAgICAgIHJldHVybiBgJHt0aGlzLmdldE1ldGVvclJlbGVhc2UoKX0uMC4wYC5tYXRjaCgvKF5cXGQrXFwuXFxkK1xcLlxcZCspL2dtaSlbMF07XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVmFsaWRhdGUgbWV0ZW9yIHZlcnNpb24gYWdhaW5zdCBhIHZlcnNpb25SYW5nZS5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gdmVyc2lvblJhbmdlIC0gc2VtdmVyIHZlcnNpb24gcmFuZ2VcbiAgICAgKi9cbiAgICBjaGVja01ldGVvclZlcnNpb24odmVyc2lvblJhbmdlKSB7XG4gICAgICAgIGNvbnN0IHJlbGVhc2UgPSB0aGlzLmNhc3RNZXRlb3JSZWxlYXNlVG9TZW12ZXIoKTtcbiAgICAgICAgaWYgKCFzZW12ZXIuc2F0aXNmaWVzKHJlbGVhc2UsIHZlcnNpb25SYW5nZSkpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLiQuZW52Lm9wdGlvbnMuc2tpcE1vYmlsZUJ1aWxkKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2cuZXJyb3IoYHdyb25nIG1ldGVvciB2ZXJzaW9uICgke3JlbGVhc2V9KSBpbiBwcm9qZWN0IC0gb25seSBgICtcbiAgICAgICAgICAgICAgICAgICAgYCR7dmVyc2lvblJhbmdlfSBpcyBzdXBwb3J0ZWRgKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2cuZXJyb3IoYHdyb25nIG1ldGVvciB2ZXJzaW9uICgke3JlbGVhc2V9KSBpbiBwcm9qZWN0IC0gb25seSBgICtcbiAgICAgICAgICAgICAgICAgICAgYCR7dmVyc2lvblJhbmdlfSBpcyBzdXBwb3J0ZWQgZm9yIGF1dG9tYXRpYyBtZXRlb3IgYnVpbGRzICh5b3UgY2FuIGFsd2F5cyBgICtcbiAgICAgICAgICAgICAgICAgICAgJ3RyeSB3aXRoIGAtLXNraXAtbW9iaWxlLWJ1aWxkYCBpZiB5b3UgYXJlIHVzaW5nIG1ldGVvciA+PSAxLjIuMScpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcHJvY2Vzcy5leGl0KDEpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRGVjaWRlcyB3aGljaCBzdHJhdGVneSB0byB1c2Ugd2hpbGUgdHJ5aW5nIHRvIGdldCBjbGllbnQgYnVpbGQgb3V0IG9mIE1ldGVvciBwcm9qZWN0LlxuICAgICAqIEByZXR1cm5zIHtudW1iZXJ9XG4gICAgICovXG4gICAgY2hvb3NlU3RyYXRlZ3koKSB7XG4gICAgICAgIGlmICh0aGlzLiQuZW52Lm9wdGlvbnMuZm9yY2VDb3Jkb3ZhQnVpbGQpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmluZGV4SFRNTFN0cmF0ZWdpZXMuSU5ERVhfRlJPTV9DT1JET1ZBX0JVSUxEO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgcmVsZWFzZSA9IHRoaXMuY2FzdE1ldGVvclJlbGVhc2VUb1NlbXZlcigpO1xuICAgICAgICBpZiAoc2VtdmVyLnNhdGlzZmllcyhyZWxlYXNlLCAnPiAxLjMuNCcpKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5pbmRleEhUTUxTdHJhdGVnaWVzLklOREVYX0ZST01fUlVOTklOR19TRVJWRVI7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHNlbXZlci5zYXRpc2ZpZXMocmVsZWFzZSwgJzEuMy40JykpIHtcbiAgICAgICAgICAgIGNvbnN0IGV4cGxvZGVkVmVyc2lvbiA9IHRoaXMuZ2V0TWV0ZW9yUmVsZWFzZSgpLnNwbGl0KCcuJyk7XG4gICAgICAgICAgICBpZiAoZXhwbG9kZWRWZXJzaW9uLmxlbmd0aCA+PSA0KSB7XG4gICAgICAgICAgICAgICAgaWYgKGV4cGxvZGVkVmVyc2lvblszXSA+IDEpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuaW5kZXhIVE1MU3RyYXRlZ2llcy5JTkRFWF9GUk9NX1JVTk5JTkdfU0VSVkVSO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5pbmRleEhUTUxTdHJhdGVnaWVzLklOREVYX0ZST01fQ09SRE9WQV9CVUlMRDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcy5pbmRleEhUTUxTdHJhdGVnaWVzLklOREVYX0ZST01fQ09SRE9WQV9CVUlMRDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDaGVja3MgcmVxdWlyZWQgcHJlY29uZGl0aW9ucy5cbiAgICAgKiAtIE1ldGVvciB2ZXJzaW9uXG4gICAgICogLSBpcyBtb2JpbGUgcGxhdGZvcm0gYWRkZWRcbiAgICAgKi9cbiAgICBhc3luYyBjaGVja1ByZWNvbmRpdGlvbnMoKSB7XG4gICAgICAgIGlmICh0aGlzLiQuZW52Lm9wdGlvbnMuc2tpcE1vYmlsZUJ1aWxkKSB7XG4gICAgICAgICAgICB0aGlzLmNoZWNrTWV0ZW9yVmVyc2lvbignPj0gMS4yLjEnKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuY2hlY2tNZXRlb3JWZXJzaW9uKCc+PSAxLjMuMycpO1xuICAgICAgICAgICAgdGhpcy5pbmRleEhUTUxzdHJhdGVneSA9IHRoaXMuY2hvb3NlU3RyYXRlZ3koKTtcbiAgICAgICAgICAgIGlmICh0aGlzLmluZGV4SFRNTHN0cmF0ZWd5ID09PSB0aGlzLmluZGV4SFRNTFN0cmF0ZWdpZXMuSU5ERVhfRlJPTV9DT1JET1ZBX0JVSUxEKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2cuZGVidWcoXG4gICAgICAgICAgICAgICAgICAgICdtZXRlb3IgdmVyc2lvbiBpcyA8IDEuMy40LjIgc28gdGhlIGluZGV4Lmh0bWwgZnJvbSBjb3Jkb3ZhLWJ1aWxkIHdpbGwnICtcbiAgICAgICAgICAgICAgICAgICAgJyBiZSB1c2VkJ1xuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRoaXMubG9nLmRlYnVnKFxuICAgICAgICAgICAgICAgICAgICAnbWV0ZW9yIHZlcnNpb24gaXMgPj0gMS4zLjQuMiBzbyB0aGUgaW5kZXguaHRtbCB3aWxsIGJlIGRvd25sb2FkZWQgJyArXG4gICAgICAgICAgICAgICAgICAgICdmcm9tIF9fY29yZG92YS9pbmRleC5odG1sJ1xuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIXRoaXMuJC5lbnYub3B0aW9ucy5za2lwTW9iaWxlQnVpbGQpIHtcbiAgICAgICAgICAgIGNvbnN0IHBsYXRmb3JtcyA9IGZzLnJlYWRGaWxlU3luYyh0aGlzLiQuZW52LnBhdGhzLm1ldGVvckFwcC5wbGF0Zm9ybXMsICdVVEYtOCcpO1xuICAgICAgICAgICAgaWYgKCF+cGxhdGZvcm1zLmluZGV4T2YoJ2FuZHJvaWQnKSAmJiAhfnBsYXRmb3Jtcy5pbmRleE9mKCdpb3MnKSkge1xuICAgICAgICAgICAgICAgIGlmICghdGhpcy4kLmVudi5vcHRpb25zLmFuZHJvaWQpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5tb2JpbGVQbGF0Zm9ybSA9ICdpb3MnO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMubW9iaWxlUGxhdGZvcm0gPSAnYW5kcm9pZCc7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHRoaXMubG9nLndhcm4oYG5vIG1vYmlsZSB0YXJnZXQgZGV0ZWN0ZWQgLSB3aWxsIGFkZCAnJHt0aGlzLm1vYmlsZVBsYXRmb3JtfScgYCArXG4gICAgICAgICAgICAgICAgICAgICdqdXN0IHRvIGdldCBhIG1vYmlsZSBidWlsZCcpO1xuICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgIGF3YWl0IHRoaXMuYWRkTW9iaWxlUGxhdGZvcm0odGhpcy5tb2JpbGVQbGF0Zm9ybSk7XG4gICAgICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmxvZy5lcnJvcignZmFpbGVkIHRvIGFkZCBhIG1vYmlsZSBwbGF0Zm9ybSAtIHBsZWFzZSB0cnkgdG8gZG8gaXQgbWFudWFsbHknKTtcbiAgICAgICAgICAgICAgICAgICAgcHJvY2Vzcy5leGl0KDEpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFRyaWVzIHRvIGFkZCBhIG1vYmlsZSBwbGF0Zm9ybSB0byBtZXRlb3IgcHJvamVjdC5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gcGxhdGZvcm0gLSBwbGF0Zm9ybSB0byBhZGRcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZX1cbiAgICAgKi9cbiAgICBhZGRNb2JpbGVQbGF0Zm9ybShwbGF0Zm9ybSkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5sb2cudmVyYm9zZShgYWRkaW5nIG1vYmlsZSBwbGF0Zm9ybTogJHtwbGF0Zm9ybX1gKTtcbiAgICAgICAgICAgIHNwYXduKCdtZXRlb3InLCBbJ2FkZC1wbGF0Zm9ybScsIHBsYXRmb3JtXSwge1xuICAgICAgICAgICAgICAgIGN3ZDogdGhpcy4kLmVudi5wYXRocy5tZXRlb3JBcHAucm9vdCxcbiAgICAgICAgICAgICAgICBzdGRpbzogdGhpcy4kLmVudi5zdGRpb1xuICAgICAgICAgICAgfSkub24oJ2V4aXQnLCAoKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgcGxhdGZvcm1zID0gZnMucmVhZEZpbGVTeW5jKHRoaXMuJC5lbnYucGF0aHMubWV0ZW9yQXBwLnBsYXRmb3JtcywgJ1VURi04Jyk7XG4gICAgICAgICAgICAgICAgaWYgKCF+cGxhdGZvcm1zLmluZGV4T2YoJ2FuZHJvaWQnKSAmJiAhfnBsYXRmb3Jtcy5pbmRleE9mKCdpb3MnKSkge1xuICAgICAgICAgICAgICAgICAgICByZWplY3QoKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICByZXNvbHZlKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFRyaWVzIHRvIHJlbW92ZSBhIG1vYmlsZSBwbGF0Zm9ybSBmcm9tIG1ldGVvciBwcm9qZWN0LlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBwbGF0Zm9ybSAtIHBsYXRmb3JtIHRvIHJlbW92ZVxuICAgICAqIEByZXR1cm5zIHtQcm9taXNlfVxuICAgICAqL1xuICAgIHJlbW92ZU1vYmlsZVBsYXRmb3JtKHBsYXRmb3JtKSB7XG4gICAgICAgIGlmICh0aGlzLiQuZW52Lm9wdGlvbnMuc2tpcFJlbW92ZU1vYmlsZVBsYXRmb3JtKSB7XG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIHRoaXMubG9nLnZlcmJvc2UoYHJlbW92aW5nIG1vYmlsZSBwbGF0Zm9ybTogJHtwbGF0Zm9ybX1gKTtcbiAgICAgICAgICAgIHNwYXduKCdtZXRlb3InLCBbJ3JlbW92ZS1wbGF0Zm9ybScsIHBsYXRmb3JtXSwge1xuICAgICAgICAgICAgICAgIGN3ZDogdGhpcy4kLmVudi5wYXRocy5tZXRlb3JBcHAucm9vdCxcbiAgICAgICAgICAgICAgICBzdGRpbzogdGhpcy4kLmVudi5zdGRpbyxcbiAgICAgICAgICAgICAgICBlbnY6IE9iamVjdC5hc3NpZ24oeyBNRVRFT1JfUFJFVFRZX09VVFBVVDogMCB9LCBwcm9jZXNzLmVudilcbiAgICAgICAgICAgIH0pLm9uKCdleGl0JywgKCkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IHBsYXRmb3JtcyA9IGZzLnJlYWRGaWxlU3luYyh0aGlzLiQuZW52LnBhdGhzLm1ldGVvckFwcC5wbGF0Zm9ybXMsICdVVEYtOCcpO1xuICAgICAgICAgICAgICAgIGlmICh+cGxhdGZvcm1zLmluZGV4T2YocGxhdGZvcm0pKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlamVjdCgpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogSnVzdCBjaGVja3MgZm9yIGluZGV4Lmh0bWwgYW5kIHByb2dyYW0uanNvbiBleGlzdGVuY2UuXG4gICAgICogQHJldHVybnMge2Jvb2xlYW59XG4gICAgICovXG4gICAgaXNDb3Jkb3ZhQnVpbGRSZWFkeSgpIHtcbiAgICAgICAgaWYgKHRoaXMuaW5kZXhIVE1Mc3RyYXRlZ3kgPT09IHRoaXMuaW5kZXhIVE1MU3RyYXRlZ2llcy5JTkRFWF9GUk9NX0NPUkRPVkFfQlVJTEQpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLiQudXRpbHMuZXhpc3RzKHRoaXMuJC5lbnYucGF0aHMubWV0ZW9yQXBwLmNvcmRvdmFCdWlsZEluZGV4KSAmJlxuICAgICAgICAgICAgICAgIHRoaXMuJC51dGlscy5leGlzdHModGhpcy4kLmVudi5wYXRocy5tZXRlb3JBcHAuY29yZG92YUJ1aWxkUHJvZ3JhbUpzb24pICYmXG4gICAgICAgICAgICAgICAgKFxuICAgICAgICAgICAgICAgICAgICAhdGhpcy5vbGRNYW5pZmVzdCB8fFxuICAgICAgICAgICAgICAgICAgICAodGhpcy5vbGRNYW5pZmVzdCAmJlxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5vbGRNYW5pZmVzdCAhPT0gZnMucmVhZEZpbGVTeW5jKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuJC5lbnYucGF0aHMubWV0ZW9yQXBwLmNvcmRvdmFCdWlsZFByb2dyYW1Kc29uLCAnVVRGLTgnXG4gICAgICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLiQudXRpbHMuZXhpc3RzKHRoaXMuJC5lbnYucGF0aHMubWV0ZW9yQXBwLndlYkNvcmRvdmFQcm9ncmFtSnNvbikgJiZcbiAgICAgICAgICAgIChcbiAgICAgICAgICAgICAgICAhdGhpcy5vbGRNYW5pZmVzdCB8fFxuICAgICAgICAgICAgICAgICh0aGlzLm9sZE1hbmlmZXN0ICYmXG4gICAgICAgICAgICAgICAgICAgIHRoaXMub2xkTWFuaWZlc3QgIT09IGZzLnJlYWRGaWxlU3luYyhcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuJC5lbnYucGF0aHMubWV0ZW9yQXBwLndlYkNvcmRvdmFQcm9ncmFtSnNvbiwgJ1VURi04J1xuICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBGZXRjaGVzIGluZGV4Lmh0bWwgZnJvbSBydW5uaW5nIHByb2plY3QuXG4gICAgICogQHJldHVybnMge1Byb21pc2UuPCo+fVxuICAgICAqL1xuICAgIGFzeW5jIGFjcXVpcmVJbmRleCgpIHtcbiAgICAgICAgY29uc3QgcG9ydCA9ICh0aGlzLiQuZW52Lm9wdGlvbnMucG9ydCkgPyB0aGlzLiQuZW52Lm9wdGlvbnMucG9ydCA6IDMwODA7XG4gICAgICAgIHRoaXMubG9nLmluZm8oJ2FjcXVpcmluZyBpbmRleC5odG1sJyk7XG4gICAgICAgIGNvbnN0IHJlcyA9IGF3YWl0IGZldGNoKGBodHRwOi8vMTI3LjAuMC4xOiR7cG9ydH0vX19jb3Jkb3ZhL2luZGV4Lmh0bWxgKTtcbiAgICAgICAgY29uc3QgdGV4dCA9IGF3YWl0IHJlcy50ZXh0KCk7XG4gICAgICAgIC8vIFNpbXBsZSB0ZXN0IGlmIHdlIHJlYWxseSBkb3dubG9hZCBpbmRleC5odG1sIGZvciB3ZWIuY29yZG92YS5cbiAgICAgICAgaWYgKH50ZXh0LmluZGV4T2YoJ3NyYz1cIi9jb3Jkb3ZhLmpzXCInKSkge1xuICAgICAgICAgICAgcmV0dXJuIHRleHQ7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEZldGNoZXMgbWFpbmZlc3QuanNvbiBmcm9tIHJ1bm5pbmcgcHJvamVjdC5cbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZS48dm9pZD59XG4gICAgICovXG4gICAgYXN5bmMgYWNxdWlyZU1hbmlmZXN0KCkge1xuICAgICAgICBjb25zdCBwb3J0ID0gKHRoaXMuJC5lbnYub3B0aW9ucy5wb3J0KSA/IHRoaXMuJC5lbnYub3B0aW9ucy5wb3J0IDogMzA4MDtcbiAgICAgICAgdGhpcy5sb2cuaW5mbygnYWNxdWlyaW5nIG1hbmlmZXN0Lmpzb24nKTtcbiAgICAgICAgY29uc3QgcmVzID0gYXdhaXQgZmV0Y2goXG4gICAgICAgICAgICBgaHR0cDovLzEyNy4wLjAuMToke3BvcnR9L19fY29yZG92YS9tYW5pZmVzdC5qc29uP21ldGVvcl9kb250X3NlcnZlX2luZGV4PXRydWVgXG4gICAgICAgICk7XG4gICAgICA