'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.events = exports.NO_PENDING_UPDATES = exports.INSTALLING_MODULE_PROGRESS = exports.INSTALLING_MODULE = exports.INSTALLING_MODULES_FINISHED = exports.DOWNLOADED_MODULE = exports.UPDATE_MANUALLY = exports.DOWNLOADING_MODULES_FINISHED = exports.DOWNLOADING_MODULE_PROGRESS = exports.DOWNLOADING_MODULE = exports.UPDATE_CHECK_FINISHED = exports.INSTALLED_MODULE = exports.CHECKING_FOR_UPDATES = undefined; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; exports.initPathsOnly = initPathsOnly; exports.init = init; exports.checkForUpdates = checkForUpdates; exports.quitAndInstallUpdates = quitAndInstallUpdates; exports.isInstalled = isInstalled; exports.getInstalled = getInstalled; exports.install = install; exports.installPendingUpdates = installPendingUpdates; var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs); var _path = require('path'); var _path2 = _interopRequireDefault(_path); var _module = require('module'); var _module2 = _interopRequireDefault(_module); var _events = require('events'); var _mkdirp = require('mkdirp'); var _mkdirp2 = _interopRequireDefault(_mkdirp); var _yauzl = require('yauzl'); var _yauzl2 = _interopRequireDefault(_yauzl); var _paths = require('./paths'); var paths = _interopRequireWildcard(_paths); var _Backoff = require('./Backoff'); var _Backoff2 = _interopRequireDefault(_Backoff); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Manages additional module installation and management. // We add the module folder path to require() lookup paths here. // undocumented node API var originalFs = require('original-fs'); var Events = function (_EventEmitter) { _inherits(Events, _EventEmitter); function Events() { _classCallCheck(this, Events); return _possibleConstructorReturn(this, (Events.__proto__ || Object.getPrototypeOf(Events)).apply(this, arguments)); } _createClass(Events, [{ key: 'emit', value: function emit() { var _this2 = this; for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } process.nextTick(function () { return _get(Events.prototype.__proto__ || Object.getPrototypeOf(Events.prototype), 'emit', _this2).apply(_this2, args); }); } }]); return Events; }(_events.EventEmitter); var LogStream = function () { function LogStream(logPath) { _classCallCheck(this, LogStream); try { this.logStream = _fs2.default.createWriteStream(logPath, { flags: 'a' }); } catch (e) { console.error('Failed to create ' + logPath + ': ' + String(e)); } } _createClass(LogStream, [{ key: 'log', value: function log(message) { message = '[Modules] ' + message; console.log(message); if (this.logStream) { this.logStream.write(message); this.logStream.write('\r\n'); } } }, { key: 'end', value: function end() { if (this.logStream) { this.logStream.end(); this.logStream = null; } } }]); return LogStream; }(); // events var CHECKING_FOR_UPDATES = exports.CHECKING_FOR_UPDATES = 'checking-for-updates'; var INSTALLED_MODULE = exports.INSTALLED_MODULE = 'installed-module'; var UPDATE_CHECK_FINISHED = exports.UPDATE_CHECK_FINISHED = 'update-check-finished'; var DOWNLOADING_MODULE = exports.DOWNLOADING_MODULE = 'downloading-module'; var DOWNLOADING_MODULE_PROGRESS = exports.DOWNLOADING_MODULE_PROGRESS = 'downloading-module-progress'; var DOWNLOADING_MODULES_FINISHED = exports.DOWNLOADING_MODULES_FINISHED = 'downloading-modules-finished'; var UPDATE_MANUALLY = exports.UPDATE_MANUALLY = 'update-manually'; var DOWNLOADED_MODULE = exports.DOWNLOADED_MODULE = 'downloaded-module'; var INSTALLING_MODULES_FINISHED = exports.INSTALLING_MODULES_FINISHED = 'installing-modules-finished'; var INSTALLING_MODULE = exports.INSTALLING_MODULE = 'installing-module'; var INSTALLING_MODULE_PROGRESS = exports.INSTALLING_MODULE_PROGRESS = 'installing-module-progress'; var NO_PENDING_UPDATES = exports.NO_PENDING_UPDATES = 'no-pending-updates'; // settings var ALWAYS_ALLOW_UPDATES = 'ALWAYS_ALLOW_UPDATES'; var SKIP_HOST_UPDATE = 'SKIP_HOST_UPDATE'; var SKIP_MODULE_UPDATE = 'SKIP_MODULE_UPDATE'; var ALWAYS_BOOTSTRAP_MODULES = 'ALWAYS_BOOTSTRAP_MODULES'; var USE_LOCAL_MODULE_VERSIONS = 'USE_LOCAL_MODULE_VERSIONS'; var request = require('../app_bootstrap/request'); var REQUEST_TIMEOUT = 15000; var backoff = new _Backoff2.default(1000, 20000); var events = exports.events = new Events(); var logger = void 0; var locallyInstalledModules = void 0; var moduleInstallPath = void 0; var installedModulesFilePath = void 0; var moduleDownloadPath = void 0; var bootstrapping = void 0; var hostUpdater = void 0; var hostUpdateAvailable = void 0; var skipHostUpdate = void 0; var skipModuleUpdate = void 0; var checkingForUpdates = void 0; var remoteBaseURL = void 0; var remoteQuery = void 0; var settings = void 0; var remoteModuleVersions = void 0; var installedModules = void 0; var download = void 0; var unzip = void 0; var newInstallInProgress = void 0; var localModuleVersionsFilePath = void 0; var updatable = void 0; var bootstrapManifestFilePath = void 0; function initPathsOnly(_buildInfo) { if (locallyInstalledModules || moduleInstallPath) { return; } // If we have `localModulesRoot` in our buildInfo file, we do not fetch modules // from remote, and rely on our locally bundled ones. // Typically used for development mode, or private builds. locallyInstalledModules = _buildInfo.localModulesRoot != null; if (locallyInstalledModules) { if (_module2.default.globalPaths.indexOf(_buildInfo.localModulesRoot) === -1) { _module2.default.globalPaths.push(_buildInfo.localModulesRoot); } } else { moduleInstallPath = _path2.default.join(paths.getUserDataVersioned(), 'modules'); if (_module2.default.globalPaths.indexOf(moduleInstallPath) === -1) { _module2.default.globalPaths.push(moduleInstallPath); } } } function init(_endpoint, _settings, _buildInfo) { var endpoint = _endpoint; settings = _settings; var buildInfo = _buildInfo; updatable = buildInfo.version != '0.0.0' && !buildInfo.debug || settings.get(ALWAYS_ALLOW_UPDATES); initPathsOnly(buildInfo); logger = new LogStream(_path2.default.join(paths.getUserData(), 'modules.log')); bootstrapping = false; hostUpdateAvailable = false; checkingForUpdates = false; skipHostUpdate = settings.get(SKIP_HOST_UPDATE) || !updatable; skipModuleUpdate = settings.get(SKIP_MODULE_UPDATE) || locallyInstalledModules || !updatable; localModuleVersionsFilePath = _path2.default.join(paths.getUserData(), 'local_module_versions.json'); bootstrapManifestFilePath = _path2.default.join(paths.getResources(), 'bootstrap', 'manifest.json'); installedModules = {}; remoteModuleVersions = {}; newInstallInProgress = {}; download = { // currently downloading active: false, // {name, version} queue: [], // current queue index being downloaded next: 0, // download failure count failures: 0 }; unzip = { // currently unzipping active: false, // {name, version, zipfile} queue: [], // current queue index being unzipped next: 0, // unzip failure count failures: 0 }; logger.log('Modules initializing'); logger.log('Distribution: ' + (locallyInstalledModules ? 'local' : 'remote')); logger.log('Host updates: ' + (skipHostUpdate ? 'disabled' : 'enabled')); logger.log('Module updates: ' + (skipModuleUpdate ? 'disabled' : 'enabled')); if (!locallyInstalledModules) { installedModulesFilePath = _path2.default.join(moduleInstallPath, 'installed.json'); moduleDownloadPath = _path2.default.join(moduleInstallPath, 'pending'); _mkdirp2.default.sync(moduleDownloadPath); logger.log('Module install path: ' + moduleInstallPath); logger.log('Module installed file path: ' + installedModulesFilePath); logger.log('Module download path: ' + moduleDownloadPath); var failedLoadingInstalledModules = false; try { installedModules = JSON.parse(_fs2.default.readFileSync(installedModulesFilePath)); } catch (err) { failedLoadingInstalledModules = true; } cleanDownloadedModules(installedModules); bootstrapping = failedLoadingInstalledModules || settings.get(ALWAYS_BOOTSTRAP_MODULES); } hostUpdater = require('../app_bootstrap/hostUpdater'); // TODO: hostUpdater constants hostUpdater.on('checking-for-update', function () { return events.emit(CHECKING_FOR_UPDATES); }); hostUpdater.on('update-available', function () { return hostOnUpdateAvailable(); }); hostUpdater.on('update-progress', function (progress) { return hostOnUpdateProgress(progress); }); hostUpdater.on('update-not-available', function () { return hostOnUpdateNotAvailable(); }); hostUpdater.on('update-manually', function (newVersion) { return hostOnUpdateManually(newVersion); }); hostUpdater.on('update-downloaded', function () { return hostOnUpdateDownloaded(); }); hostUpdater.on('error', function (err) { return hostOnError(err); }); var setFeedURL = hostUpdater.setFeedURL.bind(hostUpdater); remoteBaseURL = endpoint + '/modules/' + buildInfo.releaseChannel; // eslint-disable-next-line camelcase remoteQuery = { host_version: buildInfo.version }; switch (process.platform) { case 'darwin': setFeedURL(endpoint + '/updates/' + buildInfo.releaseChannel + '?platform=osx&version=' + buildInfo.version); remoteQuery.platform = 'osx'; break; case 'win32': // Squirrel for Windows can't handle query params // https://github.com/Squirrel/Squirrel.Windows/issues/132 setFeedURL(endpoint + '/updates/' + buildInfo.releaseChannel); remoteQuery.platform = 'win'; break; case 'linux': setFeedURL(endpoint + '/updates/' + buildInfo.releaseChannel + '?platform=linux&version=' + buildInfo.version); remoteQuery.platform = 'linux'; break; } } function cleanDownloadedModules(installedModules) { try { var entries = _fs2.default.readdirSync(moduleDownloadPath) || []; entries.forEach(function (entry) { var entryPath = _path2.default.join(moduleDownloadPath, entry); var isStale = true; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = Object.keys(installedModules)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var moduleName = _step.value; if (entryPath === installedModules[moduleName].updateZipfile) { isStale = false; break; } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } if (isStale) { _fs2.default.unlinkSync(_path2.default.join(moduleDownloadPath, entry)); } }); } catch (err) { logger.log('Could not clean downloaded modules'); logger.log(err.stack); } } function hostOnUpdateAvailable() { logger.log('Host update is available.'); hostUpdateAvailable = true; events.emit(UPDATE_CHECK_FINISHED, true, 1, false); events.emit(DOWNLOADING_MODULE, 'host', 1, 1); } function hostOnUpdateProgress(progress) { logger.log('Host update progress: ' + progress + '%'); events.emit(DOWNLOADING_MODULE_PROGRESS, 'host', progress); } function hostOnUpdateNotAvailable() { logger.log('Host is up to date.'); if (!skipModuleUpdate) { checkForModuleUpdates(); } else { events.emit(UPDATE_CHECK_FINISHED, true, 0, false); } } function hostOnUpdateManually(newVersion) { logger.log('Host update is available. Manual update required!'); hostUpdateAvailable = true; checkingForUpdates = false; events.emit(UPDATE_MANUALLY, newVersion); events.emit(UPDATE_CHECK_FINISHED, true, 1, true); } function hostOnUpdateDownloaded() { logger.log('Host update downloaded.'); checkingForUpdates = false; events.emit(DOWNLOADED_MODULE, 'host', 1, 1, true); events.emit(DOWNLOADING_MODULES_FINISHED, 1, 0); } function hostOnError(err) { logger.log('Host update failed: ' + err); // [adill] osx unsigned builds will fire this code signing error inside setFeedURL and // if we don't do anything about it hostUpdater.checkForUpdates() will never respond. if (err && String(err).indexOf('Could not get code signature for running application') !== -1) { console.warn('Skipping host updates due to code signing failure.'); skipHostUpdate = true; } checkingForUpdates = false; if (!hostUpdateAvailable) { events.emit(UPDATE_CHECK_FINISHED, false, 0, false); } else { events.emit(DOWNLOADED_MODULE, 'host', 1, 1, false); events.emit(DOWNLOADING_MODULES_FINISHED, 0, 1); } } function checkForUpdates() { if (checkingForUpdates) return; checkingForUpdates = true; hostUpdateAvailable = false; if (skipHostUpdate) { events.emit(CHECKING_FOR_UPDATES); hostOnUpdateNotAvailable(); } else { logger.log('Checking for host updates.'); hostUpdater.checkForUpdates(); } } function getRemoteModuleName(name) { if (process.platform === 'win32' && process.arch === 'x64') { return name + '.x64'; } return name; } function checkForModuleUpdates() { var query = _extends({}, remoteQuery, { _: Math.floor(Date.now() / 1000 / 60 / 5) }); var url = remoteBaseURL + '/versions.json'; logger.log('Checking for module updates at ' + url); request.get({ url: url, agent: false, encoding: null, qs: query, timeout: REQUEST_TIMEOUT, strictSSL: false }, function (err, response, body) { checkingForUpdates = false; if (!err && response.statusCode !== 200) { err = new Error('Non-200 response code: ' + response.statusCode); } if (err) { logger.log('Failed fetching module versions: ' + String(err)); events.emit(UPDATE_CHECK_FINISHED, false, 0, false); return; } remoteModuleVersions = JSON.parse(body); if (settings.get(USE_LOCAL_MODULE_VERSIONS)) { try { remoteModuleVersions = JSON.parse(_fs2.default.readFileSync(localModuleVersionsFilePath)); console.log('Using local module versions: ', remoteModuleVersions); } catch (err) { console.warn('Failed to parse local module versions: ', err); } } var updatesToDownload = []; var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = Object.keys(installedModules)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var moduleName = _step2.value; var installedModule = installedModules[moduleName]; var installed = installedModule.installedVersion; if (installed === null) { continue; } var update = installedModule.updateVersion || 0; var remote = remoteModuleVersions[getRemoteModuleName(moduleName)] || 0; // TODO: strict equality? if (installed != remote && update != remote) { logger.log('Module update available: ' + moduleName + '@' + remote + ' [installed: ' + installed + ']'); updatesToDownload.push({ name: moduleName, version: remote }); } } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } events.emit(UPDATE_CHECK_FINISHED, true, updatesToDownload.length, false); if (updatesToDownload.length === 0) { logger.log('No module updates available.'); } else { updatesToDownload.forEach(function (e) { return addModuleToDownloadQueue(e.name, e.version); }); } }); } function addModuleToDownloadQueue(name, version, authToken) { download.queue.push({ name: name, version: version, authToken: authToken }); process.nextTick(function () { return processDownloadQueue(); }); } function processDownloadQueue() { if (download.active) return; if (download.queue.length === 0) return; download.active = true; var queuedModule = download.queue[download.next]; download.next += 1; events.emit(DOWNLOADING_MODULE, queuedModule.name, download.next, download.queue.length); var totalBytes = 1; var receivedBytes = 0; var progress = 0; var hasErrored = false; var url = remoteBaseURL + '/' + getRemoteModuleName(queuedModule.name) + '/' + queuedModule.version; logger.log('Fetching ' + queuedModule.name + '@' + queuedModule.version + ' from ' + url); var headers = {}; if (queuedModule.authToken) { headers['Authorization'] = queuedModule.authToken; } request.get({ url: url, agent: false, encoding: null, followAllRedirects: true, qs: remoteQuery, timeout: REQUEST_TIMEOUT, strictSSL: false, headers: headers }).on('error', function (err) { if (hasErrored) return; hasErrored = true; logger.log('Failed fetching ' + queuedModule.name + '@' + queuedModule.version + ': ' + String(err)); finishModuleDownload(queuedModule.name, queuedModule.version, null, false); }).on('response', function (response) { totalBytes = response.headers['content-length'] || 1; var moduleZipPath = _path2.default.join(moduleDownloadPath, queuedModule.name + '-' + queuedModule.version + '.zip'); logger.log('Streaming ' + queuedModule.name + '@' + queuedModule.version + ' [' + totalBytes + ' bytes] to ' + moduleZipPath); var stream = _fs2.default.createWriteStream(moduleZipPath); stream.on('finish', function () { return finishModuleDownload(queuedModule.name, queuedModule.version, moduleZipPath, response.statusCode === 200); }); response.on('data', function (chunk) { receivedBytes += chunk.length; stream.write(chunk); var fraction = receivedBytes / totalBytes; var newProgress = Math.min(Math.floor(100 * fraction), 100); if (progress != newProgress) { progress = newProgress; events.emit(DOWNLOADING_MODULE_PROGRESS, queuedModule.name, progress); } }); // TODO: on response error // TODO: on stream error response.on('end', function () { return stream.end(); }); }); } function commitInstalledModules() { var data = JSON.stringify(installedModules, null, 2); _fs2.default.writeFileSync(installedModulesFilePath, data); } function finishModuleDownload(name, version, zipfile, succeeded) { if (!installedModules[name]) { installedModules[name] = {}; } if (succeeded) { installedModules[name].updateVersion = version; installedModules[name].updateZipfile = zipfile; commitInstalledModules(); } else { download.failures += 1; } events.emit(DOWNLOADED_MODULE, name, download.next, download.queue.length, succeeded); if (download.next >= download.queue.length) { var successes = download.queue.length - download.failures; logger.log('Finished module downloads. [success: ' + successes + '] [failure: ' + download.failures + ']'); events.emit(DOWNLOADING_MODULES_FINISHED, successes, download.failures); download.queue = []; download.next = 0; download.failures = 0; download.active = false; } else { var continueDownloads = function continueDownloads() { download.active = false; processDownloadQueue(); }; if (succeeded) { backoff.succeed(); process.nextTick(continueDownloads); } else { logger.log('Waiting ' + Math.floor(backoff.current) + 'ms before next download.'); backoff.fail(continueDownloads); } } if (newInstallInProgress[name]) { addModuleToUnzipQueue(name, version, zipfile); } } function addModuleToUnzipQueue(name, version, zipfile) { unzip.queue.push({ name: name, version: version, zipfile: zipfile }); process.nextTick(function () { return processUnzipQueue(); }); } function processUnzipQueue() { if (unzip.active) return; if (unzip.queue.length === 0) return; unzip.active = true; var queuedModule = unzip.queue[unzip.next]; unzip.next += 1; events.emit(INSTALLING_MODULE, queuedModule.name, unzip.next, unzip.queue.length); var hasErrored = false; var onError = function onError(error, zipfile) { if (hasErrored) return; hasErrored = true; logger.log('Failed installing ' + queuedModule.name + '@' + queuedModule.version + ': ' + String(error)); succeeded = false; if (zipfile) { zipfile.close(); } finishModuleUnzip(queuedModule, succeeded); }; var succeeded = true; var extractRoot = _path2.default.join(moduleInstallPath, queuedModule.name); logger.log('Installing ' + queuedModule.name + '@' + queuedModule.version + ' from ' + queuedModule.zipfile); var processZipfile = function processZipfile(err, zipfile) { if (err) { onError(err, null); return; } var totalEntries = zipfile.entryCount; var processedEntries = 0; zipfile.on('entry', function (entry) { processedEntries += 1; var percent = Math.min(Math.floor(processedEntries / totalEntries * 100), 100); events.emit(INSTALLING_MODULE_PROGRESS, queuedModule.name, percent); // skip directories if (/\/$/.test(entry.fileName)) { zipfile.readEntry(); return; } zipfile.openReadStream(entry, function (err, stream) { if (err) { onError(err, zipfile); return; } stream.on('error', function (e) { return onError(e, zipfile); }); (0, _mkdirp2.default)(_path2.default.join(extractRoot, _path2.default.dirname(entry.fileName)), function (err) { if (err) { onError(err, zipfile); return; } // [adill] createWriteStream via original-fs is broken in Electron 4.0.0-beta.6 with .asar files // so we unzip to a temporary filename and rename it afterwards var tempFileName = _path2.default.join(extractRoot, entry.fileName + '.tmp'); var finalFileName = _path2.default.join(extractRoot, entry.fileName); var writeStream = originalFs.createWriteStream(tempFileName); writeStream.on('error', function (e) { stream.destroy(); try { originalFs.unlinkSync(tempFileName); } catch (err) {} onError(e, zipfile); }); writeStream.on('finish', function () { try { originalFs.unlinkSync(finalFileName); } catch (err) {} try { originalFs.renameSync(tempFileName, finalFileName); } catch (err) { onError(err, zipfile); return; } zipfile.readEntry(); }); stream.pipe(writeStream); }); }); }); zipfile.on('error', function (err) { onError(err, zipfile); }); zipfile.on('end', function () { if (!succeeded) return; installedModules[queuedModule.name].installedVersion = queuedModule.version; finishModuleUnzip(queuedModule, succeeded); }); zipfile.readEntry(); }; try { _yauzl2.default.open(queuedModule.zipfile, { lazyEntries: true, autoClose: true }, processZipfile); } catch (err) { onError(err, null); } } function finishModuleUnzip(unzippedModule, succeeded) { delete newInstallInProgress[unzippedModule.name]; delete installedModules[unzippedModule.name].updateZipfile; delete installedModules[unzippedModule.name].updateVersion; commitInstalledModules(); if (!succeeded) { unzip.failures += 1; } events.emit(INSTALLED_MODULE, unzippedModule.name, unzip.next, unzip.queue.length, succeeded); if (unzip.next >= unzip.queue.length) { var successes = unzip.queue.length - unzip.failures; bootstrapping = false; logger.log('Finished module installations. [success: ' + successes + '] [failure: ' + unzip.failures + ']'); unzip.queue = []; unzip.next = 0; unzip.failures = 0; unzip.active = false; events.emit(INSTALLING_MODULES_FINISHED, successes, unzip.failures); return; } process.nextTick(function () { unzip.active = false; processUnzipQueue(); }); } function quitAndInstallUpdates() { logger.log('Relaunching to install ' + (hostUpdateAvailable ? 'host' : 'module') + ' updates...'); if (hostUpdateAvailable) { hostUpdater.quitAndInstall(); } else { relaunch(); } } function relaunch() { logger.end(); var _require = require('electron'), app = _require.app; app.relaunch(); app.quit(); } function isInstalled(name, version) { var metadata = installedModules[name]; if (locallyInstalledModules) return true; if (metadata && metadata.installedVersion > 0) { if (!version) return true; if (metadata.installedVersion === version) return true; } return false; } function getInstalled() { return _extends({}, installedModules); } function install(name, defer, options) { var _ref = options || {}, version = _ref.version, authToken = _ref.authToken; if (isInstalled(name, version)) { if (!defer) { events.emit(INSTALLED_MODULE, name, 1, 1, true); } return; } if (newInstallInProgress[name]) return; if (!updatable) { logger.log('Not updatable; ignoring request to install ' + name + '...'); return; } if (defer) { if (version) { throw new Error('Cannot defer install for a specific version module (' + name + ', ' + version + ')'); } logger.log('Deferred install for ' + name + '...'); installedModules[name] = { installedVersion: 0 }; commitInstalledModules(); } else { logger.log('Starting to install ' + name + '...'); if (!version) { version = remoteModuleVersions[name] || 0; } newInstallInProgress[name] = version; addModuleToDownloadQueue(name, version, authToken); } } function installPendingUpdates() { var updatesToInstall = []; if (bootstrapping) { var modules = {}; try { modules = JSON.parse(_fs2.default.readFileSync(bootstrapManifestFilePath)); } catch (err) {} var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = Object.keys(modules)[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var moduleName = _step3.value; installedModules[moduleName] = { installedVersion: 0 }; var zipfile = _path2.default.join(paths.getResources(), 'bootstrap', moduleName + '.zip'); updatesToInstall.push({ moduleName: moduleName, update: modules[moduleName], zipfile: zipfile }); } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } } var _iteratorNormalCompletion4 = true; var _didIteratorError4 = false; var _iteratorError4 = undefined; try { for (var _iterator4 = Object.keys(installedModules)[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { var _moduleName = _step4.value; var update = installedModules[_moduleName].updateVersion || 0; var _zipfile = installedModules[_moduleName].updateZipfile; if (update > 0 && _zipfile != null) { updatesToInstall.push({ moduleName: _moduleName, update: update, zipfile: _zipfile }); } } } catch (err) { _didIteratorError4 = true; _iteratorError4 = err; } finally { try { if (!_iteratorNormalCompletion4 && _iterator4.return) { _iterator4.return(); } } finally { if (_didIteratorError4) { throw _iteratorError4; } } } if (updatesToInstall.length > 0) { logger.log((bootstrapping ? 'Bootstrapping' : 'Installing updates') + '...'); updatesToInstall.forEach(function (e) { return addModuleToUnzipQueue(e.moduleName, e.update, e.zipfile); }); } else { logger.log('No updates to install'); events.emit(NO_PENDING_UPDATES); } }