"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NoOpLogger = exports.AppUpdater = void 0; function _bluebirdLst() { const data = require("bluebird-lst"); _bluebirdLst = function () { return data; }; return data; } function _builderUtilRuntime() { const data = require("builder-util-runtime"); _builderUtilRuntime = function () { return data; }; return data; } function _crypto() { const data = require("crypto"); _crypto = function () { return data; }; return data; } function _electron() { const data = require("electron"); _electron = function () { return data; }; return data; } function _electronIsDev() { const data = _interopRequireDefault(require("electron-is-dev")); _electronIsDev = function () { return data; }; return data; } function _events() { const data = require("events"); _events = function () { return data; }; return data; } function _fsExtraP() { const data = require("fs-extra-p"); _fsExtraP = function () { return data; }; return data; } function _jsYaml() { const data = require("js-yaml"); _jsYaml = function () { return data; }; return data; } function _lazyVal() { const data = require("lazy-val"); _lazyVal = function () { return data; }; return data; } var path = _interopRequireWildcard(require("path")); function _semver() { const data = require("semver"); _semver = function () { return data; }; return data; } require("source-map-support/register"); function _DownloadedUpdateHelper() { const data = require("./DownloadedUpdateHelper"); _DownloadedUpdateHelper = function () { return data; }; return data; } function _electronHttpExecutor() { const data = require("./electronHttpExecutor"); _electronHttpExecutor = function () { return data; }; return data; } function _GenericProvider() { const data = require("./providers/GenericProvider"); _GenericProvider = function () { return data; }; return data; } function _main() { const data = require("./main"); _main = function () { return data; }; return data; } function _providerFactory() { const data = require("./providerFactory"); _providerFactory = function () { return data; }; return data; } 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)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } class AppUpdater extends _events().EventEmitter { constructor(options, app) { super(); /** * Whether to automatically download an update when it is found. */ this.autoDownload = true; /** * Whether to automatically install a downloaded update on app quit (if `quitAndInstall` was not called before). * * Applicable only on Windows and Linux. */ this.autoInstallOnAppQuit = true; /** * *GitHub provider only.* Whether to allow update to pre-release versions. Defaults to `true` if application version contains prerelease components (e.g. `0.12.1-alpha.1`, here `alpha` is a prerelease component), otherwise `false`. * * If `true`, downgrade will be allowed (`allowDowngrade` will be set to `true`). */ this.allowPrerelease = false; /** * *GitHub provider only.* Get all release notes (from current version to latest), not just the latest. * @default false */ this.fullChangelog = false; /** * Whether to allow version downgrade (when a user from the beta channel wants to go back to the stable channel). * * Taken in account only if channel differs (pre-release version component in terms of semantic versioning). * * @default false */ this.allowDowngrade = false; this._channel = null; /** * The request headers. */ this.requestHeaders = null; this._logger = console; // noinspection JSUnusedGlobalSymbols /** * For type safety you can use signals, e.g. `autoUpdater.signals.updateDownloaded(() => {})` instead of `autoUpdater.on('update-available', () => {})` */ this.signals = new (_main().UpdaterSignal)(this); this._appUpdateConfigPath = null; this.clientPromise = null; this.stagingUserIdPromise = new (_lazyVal().Lazy)(() => this.getOrCreateStagingUserId()); // public, allow to read old config for anyone /** @internal */ this.configOnDisk = new (_lazyVal().Lazy)(() => this.loadUpdateConfig()); this.checkForUpdatesPromise = null; this.updateInfo = null; this.on("error", error => { this._logger.error(`Error: ${error.stack || error.message}`); }); if (app != null || global.__test_app != null) { this.app = app || global.__test_app; this.untilAppReady = Promise.resolve(); this.httpExecutor = null; } else { this.app = require("electron").app; this.httpExecutor = new (_electronHttpExecutor().ElectronHttpExecutor)((authInfo, callback) => this.emit("login", authInfo, callback)); this.untilAppReady = new Promise(resolve => { if (this.app.isReady()) { resolve(); } else { this.app.on("ready", resolve); } }); } this.downloadedUpdateHelper = new (_DownloadedUpdateHelper().DownloadedUpdateHelper)(path.join(this.app.getPath("userData"), "__update__")); const currentVersionString = this.app.getVersion(); const currentVersion = (0, _semver().parse)(currentVersionString); if (currentVersion == null) { throw (0, _builderUtilRuntime().newError)(`App version is not a valid semver version: "${currentVersionString}"`, "ERR_UPDATER_INVALID_VERSION"); } this.currentVersion = currentVersion; this.allowPrerelease = hasPrereleaseComponents(currentVersion); if (options != null) { this.setFeedURL(options); } } /** * Get the update channel. Not applicable for GitHub. Doesn't return `channel` from the update configuration, only if was previously set. */ get channel() { return this._channel; } /** * Set the update channel. Not applicable for GitHub. Overrides `channel` in the update configuration. * * `allowDowngrade` will be automatically set to `true`. If this behavior is not suitable for you, simple set `allowDowngrade` explicitly after. */ set channel(value) { if (this._channel != null) { // noinspection SuspiciousTypeOfGuard if (typeof value !== "string") { throw (0, _builderUtilRuntime().newError)(`Channel must be a string, but got: ${value}`, "ERR_UPDATER_INVALID_CHANNEL"); } else if (value.length === 0) { throw (0, _builderUtilRuntime().newError)(`Channel must be not an empty string`, "ERR_UPDATER_INVALID_CHANNEL"); } } this._channel = value; this.allowDowngrade = true; } /** * The logger. You can pass [electron-log](https://github.com/megahertz/electron-log), [winston](https://github.com/winstonjs/winston) or another logger with the following interface: `{ info(), warn(), error() }`. * Set it to `null` if you would like to disable a logging feature. */ get logger() { return this._logger; } set logger(value) { this._logger = value == null ? new NoOpLogger() : value; } // noinspection JSUnusedGlobalSymbols /** * test only * @private */ set updateConfigPath(value) { this.clientPromise = null; this._appUpdateConfigPath = value; this.configOnDisk = new (_lazyVal().Lazy)(() => this.loadUpdateConfig()); } get provider() { return this.clientPromise; } //noinspection JSMethodCanBeStatic,JSUnusedGlobalSymbols getFeedURL() { return "Deprecated. Do not use it."; } /** * Configure update provider. If value is `string`, [GenericServerOptions](/configuration/publish.md#genericserveroptions) will be set with value as `url`. * @param options If you want to override configuration in the `app-update.yml`. */ setFeedURL(options) { // https://github.com/electron-userland/electron-builder/issues/1105 let provider; if (typeof options === "string") { provider = new (_GenericProvider().GenericProvider)({ provider: "generic", url: options }, this, (0, _providerFactory().isUrlProbablySupportMultiRangeRequests)(options)); } else { provider = (0, _providerFactory().createClient)(options, this); } this.clientPromise = Promise.resolve(provider); } /** * Asks the server whether there is an update. */ checkForUpdates() { let checkForUpdatesPromise = this.checkForUpdatesPromise; if (checkForUpdatesPromise != null) { return checkForUpdatesPromise; } checkForUpdatesPromise = this._checkForUpdates(); this.checkForUpdatesPromise = checkForUpdatesPromise; const nullizePromise = () => this.checkForUpdatesPromise = null; checkForUpdatesPromise.then(nullizePromise).catch(nullizePromise); return checkForUpdatesPromise; } checkForUpdatesAndNotify() { if (_electronIsDev().default) { return Promise.resolve(null); } const checkForUpdatesPromise = this.checkForUpdates(); checkForUpdatesPromise.then(it => { const downloadPromise = it.downloadPromise; if (downloadPromise == null) { const debug = this._logger.debug; if (debug != null) { debug("checkForUpdatesAndNotify called, downloadPromise is null"); } return; } downloadPromise.then(() => { new (_electron().Notification)({ title: "A new update is ready to install", body: `${this.app.getName()} version ${it.updateInfo.version} is downloaded and will be automatically installed on exit` }).show(); }); }); return checkForUpdatesPromise; } isStagingMatch(updateInfo) { var _this = this; return (0, _bluebirdLst().coroutine)(function* () { const rawStagingPercentage = updateInfo.stagingPercentage; let stagingPercentage = rawStagingPercentage; if (stagingPercentage == null) { return true; } stagingPercentage = parseInt(stagingPercentage, 10); if (isNaN(stagingPercentage)) { _this._logger.warn(`Staging percentage is NaN: ${rawStagingPercentage}`); return true; } // convert from user 0-100 to internal 0-1 stagingPercentage = stagingPercentage / 100; const stagingUserId = yield _this.stagingUserIdPromise.value; const val = _builderUtilRuntime().UUID.parse(stagingUserId).readUInt32BE(12); const percentage = val / 0xFFFFFFFF; _this._logger.info(`Staging percentage: ${stagingPercentage}, percentage: ${percentage}, user id: ${stagingUserId}`); return percentage < stagingPercentage; })(); } _checkForUpdates() { var _this2 = this; return (0, _bluebirdLst().coroutine)(function* () { try { yield _this2.untilAppReady; _this2._logger.info("Checking for update"); _this2.emit("checking-for-update"); return yield _this2.doCheckForUpdates(); } catch (e) { _this2.emit("error", e, `Cannot check for updates: ${(e.stack || e).toString()}`); throw e; } })(); } computeFinalHeaders(headers) { if (this.requestHeaders != null) { Object.assign(headers, this.requestHeaders); } return headers; } isUpdateAvailable(updateInfo) { var _this3 = this; return (0, _bluebirdLst().coroutine)(function* () { const latestVersion = (0, _semver().parse)(updateInfo.version); if (latestVersion == null) { throw (0, _builderUtilRuntime().newError)(`This file could not be downloaded, or the latest version (from update server) does not have a valid semver version: "${latestVersion}"`, "ERR_UPDATER_INVALID_VERSION"); } const currentVersion = _this3.currentVersion; if ((0, _semver().eq)(latestVersion, currentVersion)) { return false; } const isStagingMatch = yield _this3.isStagingMatch(updateInfo); if (!isStagingMatch) { return false; } // https://github.com/electron-userland/electron-builder/pull/3111#issuecomment-405033227 // https://github.com/electron-userland/electron-builder/pull/3111#issuecomment-405030797 const isLatestVersionNewer = (0, _semver().gt)(latestVersion, currentVersion); if (!_this3.allowDowngrade) { return isLatestVersionNewer; } const currentVersionPrereleaseComponent = (0, _semver().prerelease)(currentVersion); const latestVersionPrereleaseComponent = (0, _semver().prerelease)(latestVersion); if (currentVersionPrereleaseComponent === latestVersionPrereleaseComponent) { // allowDowngrade taken in account only if channel differs return isLatestVersionNewer; } return true; })(); } getUpdateInfo() { var _this4 = this; return (0, _bluebirdLst().coroutine)(function* () { yield _this4.untilAppReady; if (_this4.clientPromise == null) { _this4.clientPromise = _this4.configOnDisk.value.then(it => (0, _providerFactory().createClient)(it, _this4)); } const client = yield _this4.clientPromise; const stagingUserId = yield _this4.stagingUserIdPromise.value; client.setRequestHeaders(_this4.computeFinalHeaders({ "x-user-staging-id": stagingUserId })); return yield client.getLatestVersion(); })(); } doCheckForUpdates() { var _this5 = this; return (0, _bluebirdLst().coroutine)(function* () { const updateInfo = yield _this5.getUpdateInfo(); if (!(yield _this5.isUpdateAvailable(updateInfo))) { _this5._logger.info(`Update for version ${_this5.currentVersion} is not available (latest version: ${updateInfo.version}, downgrade is ${_this5.allowDowngrade ? "allowed" : "disallowed"}).`); _this5.emit("update-not-available", updateInfo); return { versionInfo: updateInfo, updateInfo }; } _this5.updateInfo = updateInfo; _this5.onUpdateAvailable(updateInfo); const cancellationToken = new (_builderUtilRuntime().CancellationToken)(); //noinspection ES6MissingAwait return { versionInfo: updateInfo, updateInfo, cancellationToken, downloadPromise: _this5.autoDownload ? _this5.downloadUpdate(cancellationToken) : null }; })(); } onUpdateAvailable(updateInfo) { this._logger.info(`Found version ${updateInfo.version} (url: ${(0, _builderUtilRuntime().asArray)(updateInfo.files).map(it => it.url).join(", ")})`); this.emit("update-available", updateInfo); } /** * Start downloading update manually. You can use this method if `autoDownload` option is set to `false`. * @returns {Promise} Path to downloaded file. */ downloadUpdate(cancellationToken = new (_builderUtilRuntime().CancellationToken)()) { var _this6 = this; return (0, _bluebirdLst().coroutine)(function* () { const updateInfo = _this6.updateInfo; if (updateInfo == null) { const error = new Error("Please check update first"); _this6.dispatchError(error); throw error; } _this6._logger.info(`Downloading update from ${(0, _builderUtilRuntime().asArray)(updateInfo.files).map(it => it.url).join(", ")}`); try { return yield _this6.doDownloadUpdate({ updateInfo, requestHeaders: yield _this6.computeRequestHeaders(), cancellationToken }); } catch (e) { _this6.dispatchError(e); throw e; } })(); } dispatchError(e) { this.emit("error", e, (e.stack || e).toString()); } loadUpdateConfig() { var _this7 = this; return (0, _bluebirdLst().coroutine)(function* () { if (_this7._appUpdateConfigPath == null) { _this7._appUpdateConfigPath = _electronIsDev().default ? path.join(_this7.app.getAppPath(), "dev-app-update.yml") : path.join(process.resourcesPath, "app-update.yml"); } return (0, _jsYaml().safeLoad)((yield (0, _fsExtraP().readFile)(_this7._appUpdateConfigPath, "utf-8"))); })(); } computeRequestHeaders() { var _this8 = this; return (0, _bluebirdLst().coroutine)(function* () { const fileExtraDownloadHeaders = (yield _this8.provider).fileExtraDownloadHeaders; if (fileExtraDownloadHeaders != null) { const requestHeaders = _this8.requestHeaders; return requestHeaders == null ? fileExtraDownloadHeaders : Object.assign({}, fileExtraDownloadHeaders, requestHeaders); } return _this8.computeFinalHeaders({ accept: "*/*" }); })(); } getOrCreateStagingUserId() { var _this9 = this; return (0, _bluebirdLst().coroutine)(function* () { const file = path.join(_this9.app.getPath("userData"), ".updaterId"); try { const id = yield (0, _fsExtraP().readFile)(file, "utf-8"); if (_builderUtilRuntime().UUID.check(id)) { return id; } else { _this9._logger.warn(`Staging user id file exists, but content was invalid: ${id}`); } } catch (e) { if (e.code !== "ENOENT") { _this9._logger.warn(`Couldn't read staging user ID, creating a blank one: ${e}`); } } const id = _builderUtilRuntime().UUID.v5((0, _crypto().randomBytes)(4096), _builderUtilRuntime().UUID.OID); _this9._logger.info(`Generated new staging user ID: ${id}`); try { yield (0, _fsExtraP().outputFile)(file, id); } catch (e) { _this9._logger.warn(`Couldn't write out staging user ID: ${e}`); } return id; })(); } /** @internal */ get isAddNoCacheQuery() { const headers = this.requestHeaders; // https://github.com/electron-userland/electron-builder/issues/3021 if (headers == null) { return true; } for (const headerName of Object.keys(headers)) { const s = headerName.toLowerCase(); if (s === "authorization" || s === "private-token") { return false; } } return true; } executeDownload(taskOptions) { var _this10 = this; return (0, _bluebirdLst().coroutine)(function* () { const fileInfo = taskOptions.fileInfo; const downloadOptions = { skipDirCreation: true, headers: taskOptions.downloadUpdateOptions.requestHeaders, cancellationToken: taskOptions.downloadUpdateOptions.cancellationToken, sha2: fileInfo.info.sha2, sha512: fileInfo.info.sha512 }; if (_this10.listenerCount(_main().DOWNLOAD_PROGRESS) > 0) { downloadOptions.onProgress = it => _this10.emit(_main().DOWNLOAD_PROGRESS, it); } const updateInfo = taskOptions.downloadUpdateOptions.updateInfo; const version = updateInfo.version; const packageInfo = fileInfo.packageInfo; function getCacheUpdateFileName() { // bloody NodeJS URL doesn't decode automatically const urlPath = decodeURIComponent(taskOptions.fileInfo.url.pathname); if (urlPath.endsWith(`.${taskOptions.fileExtension}`)) { return path.posix.basename(urlPath); } else { // url like /latest, generate name return `update.${taskOptions.fileExtension}`; } } const cacheDir = _this10.downloadedUpdateHelper.cacheDir; yield (0, _fsExtraP().ensureDir)(cacheDir); const updateFileName = getCacheUpdateFileName(); let updateFile = path.join(cacheDir, updateFileName); const packageFile = packageInfo == null ? null : path.join(cacheDir, `package-${version}${path.extname(packageInfo.path) || ".7z"}`); const done = (() => { var _ref = (0, _bluebirdLst().coroutine)(function* (isSaveCache) { _this10.downloadedUpdateHelper.setDownloadedFile(updateFile, packageFile, updateInfo, fileInfo); if (isSaveCache) { yield _this10.downloadedUpdateHelper.cacheUpdateInfo(updateFileName); } _this10.emit(_main().UPDATE_DOWNLOADED, updateInfo); yield taskOptions.done(updateFile); return packageFile == null ? [updateFile] : [updateFile, packageFile]; }); return function done(_x) { return _ref.apply(this, arguments); }; })(); const log = _this10._logger; const cachedUpdateFile = yield _this10.downloadedUpdateHelper.validateDownloadedPath(updateFile, updateInfo, fileInfo, log); if (cachedUpdateFile != null) { updateFile = cachedUpdateFile; return yield done(false); } const removeFileIfAny = (() => { var _ref2 = (0, _bluebirdLst().coroutine)(function* () { yield _this10.downloadedUpdateHelper.clear().catch(() => {// ignore }); return yield (0, _fsExtraP().unlink)(updateFile).catch(() => {// ignore }); }); return function removeFileIfAny() { return _ref2.apply(this, arguments); }; })(); // https://github.com/electron-userland/electron-builder/pull/2474#issuecomment-366481912 let nameCounter = 0; let tempUpdateFile = path.join(cacheDir, `temp-${updateFileName}`); for (let i = 0; i < 3; i++) { try { yield (0, _fsExtraP().unlink)(tempUpdateFile); } catch (e) { if (e.code === "ENOENT") { break; } log.warn(`Error on remove temp update file: ${e}`); tempUpdateFile = path.join(cacheDir, `temp-${nameCounter++}-${updateFileName}`); } } try { yield taskOptions.task(tempUpdateFile, downloadOptions, packageFile, removeFileIfAny); yield (0, _fsExtraP().rename)(tempUpdateFile, updateFile); } catch (e) { yield removeFileIfAny(); if (e instanceof _builderUtilRuntime().CancellationError) { log.info("Cancelled"); _this10.emit("update-cancelled", updateInfo); } throw e; } log.info(`New version ${version} has been downloaded to ${updateFile}`); return yield done(true); })(); } } exports.AppUpdater = AppUpdater; function hasPrereleaseComponents(version) { const versionPrereleaseComponent = (0, _semver().prerelease)(version); return versionPrereleaseComponent != null && versionPrereleaseComponent.length > 0; } /** @private */ class NoOpLogger { info(message) {// ignore } warn(message) {// ignore } error(message) {// ignore } } exports.NoOpLogger = NoOpLogger; //# sourceMappingURL=AppUpdater.js.map