UNPKG

cap-codepush

Version:

CodePush Plugin for Capacitor. Working with Capacitor 7.

442 lines 23.9 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { AcquisitionStatus } from "code-push/script/acquisition-sdk"; import { CodePushUtil } from "./codePushUtil"; import { InstallMode } from "./installMode"; import { LocalPackage } from "./localPackage"; import { NativeAppInfo } from "./nativeAppInfo"; import { CodePush as NativeCodePush } from "./nativeCodePushPlugin"; import { RemotePackage } from "./remotePackage"; import { Sdk } from "./sdk"; import { SyncStatus } from "./syncStatus"; import { Dialog } from "@capacitor/dialog"; /** * This is the entry point to Cordova CodePush SDK. * It provides the following features to the app developer: * - polling the server for new versions of the app * - notifying the plugin that the application loaded successfully after an update * - getting information about the currently deployed package */ class CodePush { /** * Notifies the plugin that the update operation succeeded and that the application is ready. * Calling this function is required on the first run after an update. On every subsequent application run, calling this function is a noop. * If using sync API, calling this function is not required since sync calls it internally. */ notifyApplicationReady() { return NativeCodePush.notifyApplicationReady(); } /** * Reloads the application. If there is a pending update package installed using ON_NEXT_RESTART or ON_NEXT_RESUME modes, the update * will be immediately visible to the user. Otherwise, calling this function will simply reload the current version of the application. */ restartApplication() { return NativeCodePush.restartApplication(); } /** * Reports an application status back to the server. * !!! This function is called from the native side, please make changes accordingly. !!! */ reportStatus(status, label, appVersion, deploymentKey, lastVersionLabelOrAppVersion, lastVersionDeploymentKey) { if (((!label && appVersion === lastVersionLabelOrAppVersion) || label === lastVersionLabelOrAppVersion) && deploymentKey === lastVersionDeploymentKey) { // No-op since the new appVersion and label is exactly the same as the previous // (the app might have been updated via a direct or HockeyApp deployment). return; } var createPackageForReporting = (label, appVersion) => { return { /* The SDK only reports the label and appVersion. The rest of the properties are added for type safety. */ label, appVersion, deploymentKey, description: null, isMandatory: false, packageHash: null, packageSize: null, failedInstall: false }; }; var reportDone = (error) => { var reportArgs = { status, label, appVersion, deploymentKey, lastVersionLabelOrAppVersion, lastVersionDeploymentKey }; if (error) { CodePushUtil.logError(`An error occurred while reporting status: ${JSON.stringify(reportArgs)}`, error); NativeCodePush.reportFailed({ statusReport: reportArgs }); } else { CodePushUtil.logMessage(`Reported status: ${JSON.stringify(reportArgs)}`); NativeCodePush.reportSucceeded({ statusReport: reportArgs }); } }; switch (status) { case ReportStatus.STORE_VERSION: Sdk.reportStatusDeploy(null, AcquisitionStatus.DeploymentSucceeded, deploymentKey, lastVersionLabelOrAppVersion, lastVersionDeploymentKey, reportDone); break; case ReportStatus.UPDATE_CONFIRMED: Sdk.reportStatusDeploy(createPackageForReporting(label, appVersion), AcquisitionStatus.DeploymentSucceeded, deploymentKey, lastVersionLabelOrAppVersion, lastVersionDeploymentKey, reportDone); break; case ReportStatus.UPDATE_ROLLED_BACK: Sdk.reportStatusDeploy(createPackageForReporting(label, appVersion), AcquisitionStatus.DeploymentFailed, deploymentKey, lastVersionLabelOrAppVersion, lastVersionDeploymentKey, reportDone); break; } } /** * Get the current package information. * * @returns The currently deployed package information. */ getCurrentPackage() { return __awaiter(this, void 0, void 0, function* () { const pendingUpdate = yield NativeAppInfo.isPendingUpdate(); var packageInfoFile = pendingUpdate ? LocalPackage.OldPackageInfoFile : LocalPackage.PackageInfoFile; return new Promise((resolve, reject) => { LocalPackage.getPackageInfoOrNull(packageInfoFile, resolve, reject); }); }); } /** * Gets the pending package information, if any. A pending package is one that has been installed but the application still runs the old code. * This happens only after a package has been installed using ON_NEXT_RESTART or ON_NEXT_RESUME mode, but the application was not restarted/resumed yet. */ getPendingPackage() { return __awaiter(this, void 0, void 0, function* () { const pendingUpdate = yield NativeAppInfo.isPendingUpdate(); if (!pendingUpdate) return null; return new Promise((resolve, reject) => { LocalPackage.getPackageInfoOrNull(LocalPackage.PackageInfoFile, resolve, reject); }); }); } /** * Checks with the CodePush server if an update package is available for download. * * @param querySuccess Callback invoked in case of a successful response from the server. * The callback takes one RemotePackage parameter. A non-null package is a valid update. * A null package means the application is up to date for the current native application version. * @param queryError Optional callback invoked in case of an error. * @param deploymentKey Optional deployment key that overrides the config.xml setting. */ checkForUpdate(querySuccess, queryError, deploymentKey) { try { const callback = (error, remotePackageOrUpdateNotification) => __awaiter(this, void 0, void 0, function* () { if (error) { CodePushUtil.invokeErrorCallback(error, queryError); } else { const appUpToDate = () => { CodePushUtil.logMessage("App is up to date."); querySuccess && querySuccess(null); }; if (remotePackageOrUpdateNotification) { if (remotePackageOrUpdateNotification.updateAppVersion) { /* There is an update available for a different version. In the current version of the plugin, we treat that as no update. */ CodePushUtil.logMessage("An update is available, but it is targeting a newer binary version than you are currently running."); appUpToDate(); } else { /* There is an update available for the current version. */ var remotePackage = remotePackageOrUpdateNotification; const installFailed = yield NativeAppInfo.isFailedUpdate(remotePackage.packageHash); var result = new RemotePackage(); result.appVersion = remotePackage.appVersion; result.deploymentKey = deploymentKey; // server does not send back the deployment key result.description = remotePackage.description; result.downloadUrl = remotePackage.downloadUrl; result.isMandatory = remotePackage.isMandatory; result.label = remotePackage.label; result.packageHash = remotePackage.packageHash; result.packageSize = remotePackage.packageSize; result.failedInstall = installFailed; CodePushUtil.logMessage("An update is available. " + JSON.stringify(result)); querySuccess && querySuccess(result); } } else { appUpToDate(); } } }); const queryUpdate = () => __awaiter(this, void 0, void 0, function* () { try { const acquisitionManager = yield Sdk.getAcquisitionManager(deploymentKey); const localPackage = yield LocalPackage.getCurrentOrDefaultPackage(); try { const currentBinaryVersion = yield NativeAppInfo.getApplicationVersion(); localPackage.appVersion = currentBinaryVersion; } catch (e) { /* Nothing to do */ /* TODO : Why ? */ } CodePushUtil.logMessage("Checking for update."); acquisitionManager.queryUpdateWithCurrentPackage(localPackage, callback); } catch (e) { CodePushUtil.invokeErrorCallback(e, queryError); } }); if (deploymentKey) { queryUpdate(); } else { NativeAppInfo.getDeploymentKey() .then((defaultDeploymentKey) => { deploymentKey = defaultDeploymentKey; queryUpdate(); }, (deploymentKeyError) => { CodePushUtil.invokeErrorCallback(deploymentKeyError, queryError); }); } } catch (e) { CodePushUtil.invokeErrorCallback(new Error("An error occurred while querying for updates." + CodePushUtil.getErrorMessage(e)), queryError); } } /** * Convenience method for installing updates in one method call. * This method is provided for simplicity, and its behavior can be replicated by using window.codePush.checkForUpdate(), RemotePackage's download() and LocalPackage's install() methods. * If another sync is already running, it yields SyncStatus.IN_PROGRESS. * * The algorithm of this method is the following: * - Checks for an update on the CodePush server. * - If an update is available * - If the update is mandatory and the alertMessage is set in options, the user will be informed that the application will be updated to the latest version. * The update package will then be downloaded and applied. * - If the update is not mandatory and the confirmMessage is set in options, the user will be asked if they want to update to the latest version. * If they decline, the syncCallback will be invoked with SyncStatus.UPDATE_IGNORED. * - Otherwise, the update package will be downloaded and applied with no user interaction. * - If no update is available on the server, the syncCallback will be invoked with the SyncStatus.UP_TO_DATE. * - If an error occurs during checking for update, downloading or installing it, the syncCallback will be invoked with the SyncStatus.ERROR. * * @param syncOptions Optional SyncOptions parameter configuring the behavior of the sync operation. * @param downloadProgress Optional callback invoked during the download process. It is called several times with one DownloadProgress parameter. */ sync(syncOptions, downloadProgress) { return __awaiter(this, void 0, void 0, function* () { return yield new Promise((resolve, reject) => { /* Check if a sync is already in progress */ if (CodePush.SyncInProgress) { /* A sync is already in progress */ CodePushUtil.logMessage("Sync already in progress."); resolve(SyncStatus.IN_PROGRESS); } /* Create a callback that resets the SyncInProgress flag when the sync is complete * If the sync status is a result status, then the sync must be complete and the flag must be updated * Otherwise, do not change the flag and trigger the syncCallback as usual */ const syncCallbackAndUpdateSyncInProgress = (err, result) => { if (err) { syncOptions.onSyncError && syncOptions.onSyncError(err); CodePush.SyncInProgress = false; reject(err); } else { /* Call the user's callback */ syncOptions.onSyncStatusChanged && syncOptions.onSyncStatusChanged(result); /* Check if the sync operation is over */ switch (result) { case SyncStatus.ERROR: case SyncStatus.UP_TO_DATE: case SyncStatus.UPDATE_IGNORED: case SyncStatus.UPDATE_INSTALLED: /* The sync has completed */ CodePush.SyncInProgress = false; resolve(result); break; default: /* The sync is not yet complete, so do nothing */ break; } } }; /* Begin the sync */ CodePush.SyncInProgress = true; this.syncInternal(syncCallbackAndUpdateSyncInProgress, syncOptions, downloadProgress); }); }); } /** * Convenience method for installing updates in one method call. * This method is provided for simplicity, and its behavior can be replicated by using window.codePush.checkForUpdate(), RemotePackage's download() and LocalPackage's install() methods. * * A helper function for the sync function. It does not check if another sync is ongoing. * * @param syncCallback Optional callback to be called with the status of the sync operation. * The callback will be called only once, and the possible statuses are defined by the SyncStatus enum. * @param syncOptions Optional SyncOptions parameter configuring the behavior of the sync operation. * @param downloadProgress Optional callback invoked during the download process. It is called several times with one DownloadProgress parameter. * */ syncInternal(syncCallback, syncOptions, downloadProgress) { /* No options were specified, use default */ const defaultSyncOptions = this.getDefaultSyncOptions(); if (!syncOptions) { syncOptions = defaultSyncOptions; } else { /* Some options were specified */ /* Handle dialog options */ const defaultDialogOptions = this.getDefaultUpdateDialogOptions(); if (syncOptions.updateDialog) { if (typeof syncOptions.updateDialog !== typeof ({})) { /* updateDialog set to true condition, use default options */ syncOptions.updateDialog = defaultDialogOptions; } else { /* some options were specified, merge with default */ CodePushUtil.copyUnassignedMembers(defaultDialogOptions, syncOptions.updateDialog); } } /* Handle other options. Dialog options will not be overwritten. */ CodePushUtil.copyUnassignedMembers(defaultSyncOptions, syncOptions); } this.notifyApplicationReady(); const onError = (error) => { CodePushUtil.logError("An error occurred during sync.", error); syncCallback && syncCallback(error, SyncStatus.ERROR); }; const onInstallSuccess = (appliedWhen) => { switch (appliedWhen) { case InstallMode.ON_NEXT_RESTART: CodePushUtil.logMessage("Update is installed and will be run on the next app restart."); break; case InstallMode.ON_NEXT_RESUME: if (syncOptions.minimumBackgroundDuration > 0) { CodePushUtil.logMessage(`Update is installed and will be run after the app has been in the background for at least ${syncOptions.minimumBackgroundDuration} seconds.`); } else { CodePushUtil.logMessage("Update is installed and will be run when the app next resumes."); } break; } syncCallback && syncCallback(null, SyncStatus.UPDATE_INSTALLED); }; const onDownloadSuccess = (localPackage) => { syncCallback && syncCallback(null, SyncStatus.INSTALLING_UPDATE); localPackage.install(syncOptions).then(onInstallSuccess, onError); }; const downloadAndInstallUpdate = (remotePackage) => { syncCallback && syncCallback(null, SyncStatus.DOWNLOADING_PACKAGE); remotePackage.download(downloadProgress).then(onDownloadSuccess, onError); }; const onUpdate = (remotePackage) => __awaiter(this, void 0, void 0, function* () { if (remotePackage === null) { /* Then the app is up to date */ syncCallback && syncCallback(null, SyncStatus.UP_TO_DATE); } else { if (remotePackage.failedInstall && syncOptions.ignoreFailedUpdates) { CodePushUtil.logMessage("An update is available, but it is being ignored due to have been previously rolled back."); syncCallback && syncCallback(null, SyncStatus.UPDATE_IGNORED); } else { if (syncOptions.updateDialog) { CodePushUtil.logMessage("Awaiting user action."); syncCallback && syncCallback(null, SyncStatus.AWAITING_USER_ACTION); const dlgOpts = syncOptions.updateDialog; if (remotePackage.isMandatory) { /* Alert user */ const message = dlgOpts.appendReleaseDescription ? dlgOpts.mandatoryUpdateMessage + dlgOpts.descriptionPrefix + remotePackage.description : dlgOpts.mandatoryUpdateMessage; yield Dialog.alert({ message, title: dlgOpts.updateTitle, buttonTitle: dlgOpts.mandatoryContinueButtonLabel }); downloadAndInstallUpdate(remotePackage); } else { /* Confirm update with user */ const message = dlgOpts.appendReleaseDescription ? dlgOpts.optionalUpdateMessage + dlgOpts.descriptionPrefix + remotePackage.description : dlgOpts.optionalUpdateMessage; const confirmResult = yield Dialog.confirm({ message, title: dlgOpts.updateTitle, okButtonTitle: dlgOpts.optionalInstallButtonLabel, cancelButtonTitle: dlgOpts.optionalIgnoreButtonLabel }); if (confirmResult.value === true) { /* Install */ downloadAndInstallUpdate(remotePackage); } else { /* Cancel */ CodePushUtil.logMessage("User cancelled the update."); syncCallback && syncCallback(null, SyncStatus.UPDATE_IGNORED); } } } else { /* No user interaction */ downloadAndInstallUpdate(remotePackage); } } } }); syncCallback && syncCallback(null, SyncStatus.CHECKING_FOR_UPDATE); this.checkForUpdate(onUpdate, onError, syncOptions.deploymentKey); } /** * Returns the default options for the CodePush sync operation. * If the options are not defined yet, the static DefaultSyncOptions member will be instantiated. */ getDefaultSyncOptions() { if (!CodePush.DefaultSyncOptions) { CodePush.DefaultSyncOptions = { ignoreFailedUpdates: true, installMode: InstallMode.ON_NEXT_RESTART, minimumBackgroundDuration: 0, mandatoryInstallMode: InstallMode.IMMEDIATE, updateDialog: false, deploymentKey: undefined }; } return CodePush.DefaultSyncOptions; } /** * Returns the default options for the update dialog. * Please note that the dialog is disabled by default. */ getDefaultUpdateDialogOptions() { if (!CodePush.DefaultUpdateDialogOptions) { CodePush.DefaultUpdateDialogOptions = { updateTitle: "Update available", mandatoryUpdateMessage: "An update is available that must be installed.", mandatoryContinueButtonLabel: "Continue", optionalUpdateMessage: "An update is available. Would you like to install it?", optionalInstallButtonLabel: "Install", optionalIgnoreButtonLabel: "Ignore", appendReleaseDescription: false, descriptionPrefix: " Description: " }; } return CodePush.DefaultUpdateDialogOptions; } } /** * Defines the application statuses reported from the native layer. * !!! This enum is defined in native code as well, please make changes accordingly. !!! */ var ReportStatus; (function (ReportStatus) { ReportStatus[ReportStatus["STORE_VERSION"] = 0] = "STORE_VERSION"; ReportStatus[ReportStatus["UPDATE_CONFIRMED"] = 1] = "UPDATE_CONFIRMED"; ReportStatus[ReportStatus["UPDATE_ROLLED_BACK"] = 2] = "UPDATE_ROLLED_BACK"; })(ReportStatus || (ReportStatus = {})); export const codePush = new CodePush(); window.codePush = codePush; //# sourceMappingURL=codePush.js.map