"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 _electronHttpExecutor() { const data = require("./electronHttpExecutor"); _electronHttpExecutor = function () { return data; }; return data; } function _GenericProvider() { const data = require("./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 _DownloadedUpdateHelper() { const data = require("./DownloadedUpdateHelper"); _DownloadedUpdateHelper = 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). * @default false */ this.allowDowngrade = false; this._channel = null; /** * The request headers. */ this.requestHeaders = null; this._logger = console; /** * 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.updateAvailable = false; this.clientPromise = null; this.stagingUserIdPromise = new (_lazyVal().Lazy)(() => this.getOrCreateStagingUserId()); // public, allow to read old config for anyone 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)(this.app.getPath("userData")); const currentVersionString = this.app.getVersion(); const currentVersion = (0, _semver().valid)(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(this.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) { 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); } 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) { this._logger.warn("checkForUpdatesAndNotify called, but 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; } getUpdateInfo() { var _this3 = this; return (0, _bluebirdLst().coroutine)(function* () { yield _this3.untilAppReady; if (_this3.clientPromise == null) { _this3.clientPromise = _this3.configOnDisk.value.then(it => (0, _providerFactory().createClient)(it, _this3)); } const client = yield _this3.clientPromise; const stagingUserId = yield _this3.stagingUserIdPromise.value; client.setRequestHeaders(_this3.computeFinalHeaders({ "X-User-Staging-Id": stagingUserId })); return yield client.getLatestVersion(); })(); } doCheckForUpdates() { var _this4 = this; return (0, _bluebirdLst().coroutine)(function* () { const updateInfo = yield _this4.getUpdateInfo(); const latestVersion = (0, _semver().valid)(updateInfo.version); if (latestVersion == null) { throw (0, _builderUtilRuntime().newError)(`Latest version (from update server) is not valid semver version: "${latestVersion}`, "ERR_UPDATER_INVALID_VERSION"); } const isStagingMatch = yield _this4.isStagingMatch(updateInfo); if (!isStagingMatch || (_this4.allowDowngrade && !hasPrereleaseComponents(latestVersion) ? (0, _semver().eq)(latestVersion, _this4.currentVersion) : !(0, _semver().gt)(latestVersion, _this4.currentVersion))) { _this4.updateAvailable = false; _this4._logger.info(`Update for version ${_this4.currentVersion} is not available (latest version: ${updateInfo.version}, downgrade is ${_this4.allowDowngrade ? "allowed" : "disallowed"}).`); _this4.emit("update-not-available", updateInfo); return { versionInfo: updateInfo, updateInfo }; } _this4.updateAvailable = true; _this4.updateInfo = updateInfo; _this4.onUpdateAvailable(updateInfo); const cancellationToken = new (_builderUtilRuntime().CancellationToken)(); //noinspection ES6MissingAwait return { versionInfo: updateInfo, updateInfo, cancellationToken, downloadPromise: _this4.autoDownload ? _this4.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 _this5 = this; return (0, _bluebirdLst().coroutine)(function* () { const updateInfo = _this5.updateInfo; if (updateInfo == null) { const error = new Error("Please check update first"); _this5.dispatchError(error); throw error; } _this5._logger.info(`Downloading update from ${(0, _builderUtilRuntime().asArray)(updateInfo.files).map(it => it.url).join(", ")}`); try { return yield _this5.doDownloadUpdate(updateInfo, cancellationToken); } catch (e) { _this5.dispatchError(e); throw e; } })(); } dispatchError(e) { this.emit("error", e, (e.stack || e).toString()); } loadUpdateConfig() { var _this6 = this; return (0, _bluebirdLst().coroutine)(function* () { if (_this6._appUpdateConfigPath == null) { _this6._appUpdateConfigPath = _electronIsDev().default ? path.join(_this6.app.getAppPath(), "dev-app-update.yml") : path.join(process.resourcesPath, "app-update.yml"); } return (0, _jsYaml().safeLoad)((yield (0, _fsExtraP().readFile)(_this6._appUpdateConfigPath, "utf-8"))); })(); } /*** @private */ computeRequestHeaders() { var _this7 = this; return (0, _bluebirdLst().coroutine)(function* () { const fileExtraDownloadHeaders = (yield _this7.provider).fileExtraDownloadHeaders; if (fileExtraDownloadHeaders != null) { const requestHeaders = _this7.requestHeaders; return requestHeaders == null ? fileExtraDownloadHeaders : Object.assign({}, fileExtraDownloadHeaders, requestHeaders); } return _this7.computeFinalHeaders({ Accept: "*/*" }); })(); } getOrCreateStagingUserId() { var _this8 = this; return (0, _bluebirdLst().coroutine)(function* () { const file = path.join(_this8.app.getPath("userData"), ".updaterId"); try { const id = yield (0, _fsExtraP().readFile)(file, "utf-8"); if (_builderUtilRuntime().UUID.check(id)) { return id; } else { _this8._logger.warn(`Staging user id file exists, but content was invalid: ${id}`); } } catch (e) { if (e.code !== "ENOENT") { _this8._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); _this8._logger.info(`Generated new staging user ID: ${id}`); try { yield (0, _fsExtraP().outputFile)(file, id); } catch (e) { _this8._logger.warn(`Couldn't write out staging user ID: ${e}`); } return id; })(); } } 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