@bazumax/capacitor-codepush
Version:
CodePush Plugin for Capacitor
1,016 lines (1,007 loc) • 85.6 kB
JavaScript
var capacitorPlugin = (function (exports, acquisitionSdk, filesystem, core, http, device, dialog) {
'use strict';
/**
* Callback / error / logging utilities.
*/
class CodePushUtil {
/**
* Performs a copy of all members of fromParameter to toParameter, with the condition that they are unassigned or null in toParameter.
*/
static copyUnassignedMembers(fromParameter, toParameter) {
for (let key in fromParameter) {
if (toParameter[key] === undefined || toParameter[key] === null) {
toParameter[key] = fromParameter[key];
}
}
}
/**
* Given two Cordova style callbacks for success and error, this function returns a node.js
* style callback where the error is the first parameter and the result the second.
*/
static getNodeStyleCallbackFor(successCallback, errorCallback) {
return (error, result) => {
if (error) {
errorCallback && errorCallback(error);
}
else {
successCallback && successCallback(result);
}
};
}
/**
* Gets the message of an error, if any. Otherwise it returns the empty string.
*/
static getErrorMessage(e) {
return e && e.message || e && e.toString() || "";
}
/**
* Logs a message using the CodePush tag.
*/
static logMessage(msg) {
console.log(CodePushUtil.TAG + " " + msg);
}
/**
* Logs an error message using the CodePush tag.
*/
static logError(message, error) {
const errorMessage = `${message || ""} ${CodePushUtil.getErrorMessage(error)}`;
const stackTrace = error && error.stack ? `. StackTrace: ${error.stack}` : "";
console.error(`${CodePushUtil.TAG} ${errorMessage}${stackTrace}`);
}
}
/**
* Tag used for logging to the console.
*/
CodePushUtil.TAG = "[CodePush]";
/**
* Logs the error to the console and then forwards it to the provided ErrorCallback, if any.
* TODO: remove me
*/
CodePushUtil.invokeErrorCallback = (error, errorCallback) => {
CodePushUtil.logError(null, error);
errorCallback && errorCallback(error);
};
/**
* Logs the error to the console and then throws the error.
*/
CodePushUtil.throwError = (error) => {
CodePushUtil.logError(null, error);
throw error;
};
/**
* Defines the available install modes for updates.
*/
exports.InstallMode = void 0;
(function (InstallMode) {
/**
* The update will be applied to the running application immediately. The application will be reloaded with the new content immediately.
*/
InstallMode[InstallMode["IMMEDIATE"] = 0] = "IMMEDIATE";
/**
* The update is downloaded but not installed immediately. The new content will be available the next time the application is started.
*/
InstallMode[InstallMode["ON_NEXT_RESTART"] = 1] = "ON_NEXT_RESTART";
/**
* The udpate is downloaded but not installed immediately. The new content will be available the next time the application is resumed or restarted, whichever event happends first.
*/
InstallMode[InstallMode["ON_NEXT_RESUME"] = 2] = "ON_NEXT_RESUME";
})(exports.InstallMode || (exports.InstallMode = {}));
var __awaiter$5 = (undefined && undefined.__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());
});
};
/**
* File utilities for CodePush.
*/
class FileUtil {
static directoryExists(directory, path) {
return __awaiter$5(this, void 0, void 0, function* () {
try {
const statResult = yield filesystem.Filesystem.stat({ directory, path });
// directory for Android, NSFileTypeDirectory for iOS
return statResult.type === "directory" || statResult.type === "NSFileTypeDirectory";
}
catch (error) {
return false;
}
});
}
static writeStringToDataFile(content, path, createIfNotExists, callback) {
FileUtil.writeStringToFile(content, filesystem.Directory.Data, path, createIfNotExists, callback);
}
static fileExists(directory, path) {
return __awaiter$5(this, void 0, void 0, function* () {
try {
const statResult = yield filesystem.Filesystem.stat({ directory, path });
// file for Android, NSFileTypeRegular for iOS
return statResult.type === "file" || statResult.type === "NSFileTypeRegular";
}
catch (error) {
return false;
}
});
}
/**
* Makes sure the given directory exists and is empty.
*/
static cleanDataDirectory(path) {
return __awaiter$5(this, void 0, void 0, function* () {
if (yield FileUtil.dataDirectoryExists(path)) {
yield FileUtil.deleteDataDirectory(path);
}
yield filesystem.Filesystem.mkdir({ directory: filesystem.Directory.Data, path, recursive: true });
const appDir = yield filesystem.Filesystem.getUri({ directory: filesystem.Directory.Data, path });
return appDir.uri;
});
}
static getUri(fsDir, path) {
return __awaiter$5(this, void 0, void 0, function* () {
const result = yield filesystem.Filesystem.getUri({ directory: fsDir, path });
return result.uri;
});
}
static getDataUri(path) {
return FileUtil.getUri(filesystem.Directory.Data, path);
}
static dataDirectoryExists(path) {
return FileUtil.directoryExists(filesystem.Directory.Data, path);
}
static copyDirectoryEntriesTo(sourceDir, destinationDir, ignoreList = []) {
return __awaiter$5(this, void 0, void 0, function* () {
/*
Native-side exception occurs while trying to copy “.DS_Store” and “__MACOSX” entries generated by macOS, so just skip them
*/
if (ignoreList.indexOf(".DS_Store") === -1) {
ignoreList.push(".DS_Store");
}
if (ignoreList.indexOf("__MACOSX") === -1) {
ignoreList.push("__MACOSX");
}
// @capacitor/filesystem plugin throw error when destination directory already exists.
if (yield FileUtil.directoryExists(destinationDir.directory, destinationDir.path)) {
const { files } = yield filesystem.Filesystem.readdir(sourceDir);
for (let i = 0; i < files.length; i++) {
// @capacitor/filesystem v4 returns object instead of string
// @ts-expect-error
const file = files[i].name;
if (ignoreList.includes(file))
continue;
const sourcePath = sourceDir.path + "/" + file;
const destPath = destinationDir.path + "/" + file;
const source = Object.assign(Object.assign({}, sourceDir), { path: sourcePath });
const destination = Object.assign(Object.assign({}, destinationDir), { path: destPath });
if (yield FileUtil.directoryExists(source.directory, source.path)) { // is directory
yield FileUtil.copyDirectoryEntriesTo(source, destination);
}
else { // is file
yield FileUtil.copy(source, destination);
}
}
}
else {
yield FileUtil.copy(sourceDir, destinationDir);
}
});
}
static copy(source, destination) {
return __awaiter$5(this, void 0, void 0, function* () {
yield filesystem.Filesystem.copy({ directory: source.directory, from: source.path, to: destination.path, toDirectory: destination.directory });
});
}
/**
* Recursively deletes the contents of a directory.
*/
static deleteDataDirectory(path) {
return __awaiter$5(this, void 0, void 0, function* () {
yield filesystem.Filesystem.rmdir({ directory: filesystem.Directory.Data, path, recursive: true }).then(() => null);
});
}
/**
* Deletes a given set of files from a directory.
*/
static deleteEntriesFromDataDirectory(dirPath, filesToDelete) {
return __awaiter$5(this, void 0, void 0, function* () {
for (const file of filesToDelete) {
const path = dirPath + "/" + file;
const fileExists = yield FileUtil.fileExists(filesystem.Directory.Data, path);
if (!fileExists)
continue;
try {
yield filesystem.Filesystem.deleteFile({ directory: filesystem.Directory.Data, path });
}
catch (error) {
/* If delete fails, silently continue */
console.log("Could not delete file: " + path);
}
}
});
}
/**
* Writes a string to a file.
*/
static writeStringToFile(data, directory, path, createIfNotExists, callback) {
return __awaiter$5(this, void 0, void 0, function* () {
try {
yield filesystem.Filesystem.writeFile({ directory, path, data, encoding: filesystem.Encoding.UTF8 });
callback(null, null);
}
catch (error) {
callback(new Error("Could write the current package information file. Error code: " + error.code), null);
}
});
}
static readFile(directory, path) {
return __awaiter$5(this, void 0, void 0, function* () {
const result = yield filesystem.Filesystem.readFile({ directory, path, encoding: filesystem.Encoding.UTF8 });
return result.data;
});
}
static readDataFile(path) {
return FileUtil.readFile(filesystem.Directory.Data, path);
}
}
// Type definitions for Apache Cordova CodePush plugin.
const CodePush$1 = /*#__PURE__*/ core.registerPlugin("CodePush");
var __awaiter$4 = (undefined && undefined.__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());
});
};
const DefaultServerUrl = "https://codepush.appcenter.ms/";
/**
* Provides information about the native app.
*/
class NativeAppInfo {
/**
* Gets the application build timestamp.
*/
static getApplicationBuildTime() {
return __awaiter$4(this, void 0, void 0, function* () {
try {
const result = yield CodePush$1.getNativeBuildTime();
return result.value;
}
catch (e) {
throw new Error("Could not get application timestamp.");
}
});
}
/**
* Gets the application version.
*/
static getApplicationVersion() {
return __awaiter$4(this, void 0, void 0, function* () {
try {
const result = yield CodePush$1.getAppVersion();
return result.value;
}
catch (e) {
throw new Error("Could not get application version.");
}
});
}
/**
* Gets a hash of the `public` folder contents compiled in the app store binary.
*/
static getBinaryHash() {
return __awaiter$4(this, void 0, void 0, function* () {
try {
const result = yield CodePush$1.getBinaryHash();
return result.value;
}
catch (e) {
throw new Error("Could not get binary hash.");
}
});
}
/**
* Gets the server URL from config.xml by calling into the native platform.
*/
static getServerURL() {
return __awaiter$4(this, void 0, void 0, function* () {
try {
const result = yield CodePush$1.getServerURL();
return result.value;
}
catch (e) {
return DefaultServerUrl;
}
});
}
/**
* Gets the deployment key from config.xml by calling into the native platform.
*/
static getDeploymentKey() {
return __awaiter$4(this, void 0, void 0, function* () {
try {
const result = yield CodePush$1.getDeploymentKey();
return result.value;
}
catch (e) {
throw new Error("Deployment key not found.");
}
});
}
/**
* Checks if a package update was previously attempted but failed for a given package hash.
* Every reverted update is stored such that the application developer has the option to ignore
* updates that previously failed. This way, an infinite update loop can be prevented in case of a bad update package.
*/
static isFailedUpdate(packageHash) {
return __awaiter$4(this, void 0, void 0, function* () {
try {
const result = yield CodePush$1.isFailedUpdate({ packageHash });
return result.value;
}
catch (e) {
/* In case of an error, return false. */
return false;
}
});
}
/**
* Checks if this is the first application run of a package after it has been applied.
* The didUpdateCallback callback can be used for migrating data from the old app version to the new one.
*
* @param packageHash The hash value of the package.
* @returns Whether it is the first run after an update.
*/
static isFirstRun(packageHash) {
return __awaiter$4(this, void 0, void 0, function* () {
try {
const result = yield CodePush$1.isFirstRun({ packageHash });
return result.value;
}
catch (e) {
/* In case of an error, return false. */
return false;
}
});
}
/**
* Checks with the native side if there is a pending update.
*/
static isPendingUpdate() {
return __awaiter$4(this, void 0, void 0, function* () {
try {
const result = yield CodePush$1.isPendingUpdate();
return result.value;
}
catch (e) {
/* In case of an error, return false. */
return false;
}
});
}
}
/**
* Base class for CodePush packages.
*/
class Package {
}
/**
* XMLHttpRequest-based implementation of Http.Requester.
*/
class HttpRequester {
constructor(contentType) {
this.contentType = contentType;
}
request(verb, url, callbackOrRequestBody, callback) {
var requestBody;
var requestCallback = callback;
// request(verb, url, callback)
if (!requestCallback && typeof callbackOrRequestBody === "function") {
requestCallback = callbackOrRequestBody;
}
// request(verb, url, requestBody, callback)
if (typeof callbackOrRequestBody === "string") {
requestBody = callbackOrRequestBody;
}
if (typeof requestBody === "string") {
try {
requestBody = JSON.parse(requestBody); // if it is stringify JSON string, parse
}
catch (e) {
// do nothing
}
}
var methodName = this.getHttpMethodName(verb);
if (methodName === null) {
return requestCallback(new Error("Method Not Allowed"), null);
}
const headers = {
"X-CodePush-Plugin-Name": "cordova-plugin-code-push",
"X-CodePush-Plugin-Version": "1.11.13",
"X-CodePush-SDK-Version": "3.1.5"
};
if (this.contentType) {
headers["Content-Type"] = this.contentType;
}
const options = {
method: methodName,
url,
headers
};
if (methodName === "GET") {
options.params = requestBody;
}
else {
options.data = requestBody;
}
http.Http.request(options).then((nativeRes) => {
if (typeof nativeRes.data === "object")
nativeRes.data = JSON.stringify(nativeRes.data);
var response = { statusCode: nativeRes.status, body: nativeRes.data };
requestCallback && requestCallback(null, response);
});
}
/**
* Gets the HTTP method name as a string.
* The reason for which this is needed is because the Http.Verb enum corresponds to integer values from native runtime.
*/
getHttpMethodName(verb) {
switch (verb) {
case 0 /* GET */:
return "GET";
case 4 /* DELETE */:
return "DELETE";
case 1 /* HEAD */:
return "HEAD";
case 8 /* PATCH */:
return "PATCH";
case 2 /* POST */:
return "POST";
case 3 /* PUT */:
return "PUT";
case 5 /* TRACE */:
case 6 /* OPTIONS */:
case 7 /* CONNECT */:
default:
return null;
}
}
}
var __awaiter$3 = (undefined && undefined.__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());
});
};
/**
* Interacts with the CodePush Acquisition SDK.
*/
class Sdk {
/**
* Reads the CodePush configuration and creates an AcquisitionManager instance using it.
*/
static getAcquisitionManager(userDeploymentKey, contentType) {
return __awaiter$3(this, void 0, void 0, function* () {
const resolveManager = () => {
if (userDeploymentKey !== Sdk.DefaultConfiguration.deploymentKey || contentType) {
var customConfiguration = {
deploymentKey: userDeploymentKey || Sdk.DefaultConfiguration.deploymentKey,
serverUrl: Sdk.DefaultConfiguration.serverUrl,
ignoreAppVersion: Sdk.DefaultConfiguration.ignoreAppVersion,
appVersion: Sdk.DefaultConfiguration.appVersion,
clientUniqueId: Sdk.DefaultConfiguration.clientUniqueId
};
var requester = new HttpRequester(contentType);
var customAcquisitionManager = new acquisitionSdk.AcquisitionManager(requester, customConfiguration);
return Promise.resolve(customAcquisitionManager);
}
else if (Sdk.DefaultConfiguration.deploymentKey) {
return Promise.resolve(Sdk.DefaultAcquisitionManager);
}
else {
return Promise.reject(new Error("No deployment key provided, please provide a default one in your config.xml or specify one in the call to checkForUpdate() or sync()."));
}
};
if (Sdk.DefaultAcquisitionManager) {
return resolveManager();
}
else {
let serverUrl = null;
try {
serverUrl = yield NativeAppInfo.getServerURL();
}
catch (e) {
throw new Error("Could not get the CodePush configuration. Please check your config.xml file.");
}
let appVersion = null;
try {
appVersion = yield NativeAppInfo.getApplicationVersion();
}
catch (e) {
throw new Error("Could not get the app version. Please check your config.xml file.");
}
let deploymentKey = null;
try {
deploymentKey = yield NativeAppInfo.getDeploymentKey();
}
catch (e) { }
const device$1 = yield device.Device.getId();
Sdk.DefaultConfiguration = {
deploymentKey,
serverUrl,
ignoreAppVersion: false,
appVersion,
clientUniqueId: device$1.uuid
};
if (deploymentKey) {
Sdk.DefaultAcquisitionManager = new acquisitionSdk.AcquisitionManager(new HttpRequester(), Sdk.DefaultConfiguration);
}
return resolveManager();
}
});
}
/**
* Reports the deployment status to the CodePush server.
*/
static reportStatusDeploy(pkg, status, currentDeploymentKey, previousLabelOrAppVersion, previousDeploymentKey, callback) {
return __awaiter$3(this, void 0, void 0, function* () {
try {
const acquisitionManager = yield Sdk.getAcquisitionManager(currentDeploymentKey, "application/json");
acquisitionManager.reportStatusDeploy(pkg, status, previousLabelOrAppVersion, previousDeploymentKey, callback);
}
catch (e) {
callback && callback(e);
}
});
}
/**
* Reports the download status to the CodePush server.
*/
static reportStatusDownload(pkg, deploymentKey, callback) {
return __awaiter$3(this, void 0, void 0, function* () {
try {
const acquisitionManager = yield Sdk.getAcquisitionManager(deploymentKey, "application/json");
acquisitionManager.reportStatusDownload(pkg, callback);
}
catch (e) {
callback && callback(new Error("An error occured while reporting the download status. " + e));
}
});
}
}
var __awaiter$2 = (undefined && undefined.__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());
});
};
/**
* Defines a local package.
*
* !! THIS TYPE IS READ FROM NATIVE CODE AS WELL. ANY CHANGES TO THIS INTERFACE NEEDS TO BE UPDATED IN NATIVE CODE !!
*/
class LocalPackage extends Package {
/**
* Applies this package to the application. The application will be reloaded with this package and on every application launch this package will be loaded.
* On the first run after the update, the application will wait for a codePush.notifyApplicationReady() call. Once this call is made, the install operation is considered a success.
* Otherwise, the install operation will be marked as failed, and the application is reverted to its previous version on the next run.
*
* @param installOptions Optional parameter used for customizing the installation behavior.
*/
install(installOptions) {
return __awaiter$2(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => __awaiter$2(this, void 0, void 0, function* () {
try {
CodePushUtil.logMessage("Installing update");
if (!installOptions) {
installOptions = LocalPackage.getDefaultInstallOptions();
}
else {
CodePushUtil.copyUnassignedMembers(LocalPackage.getDefaultInstallOptions(), installOptions);
}
var installError = (error) => {
CodePushUtil.invokeErrorCallback(error, reject);
Sdk.reportStatusDeploy(this, acquisitionSdk.AcquisitionStatus.DeploymentFailed, this.deploymentKey);
};
let unzipDir;
try {
unzipDir = yield FileUtil.cleanDataDirectory(LocalPackage.DownloadUnzipDir);
}
catch (error) {
installError(error);
return;
}
try {
yield CodePush$1.unzip({ zipFile: this.localPath, targetDirectory: unzipDir });
}
catch (unzipError) {
installError(new Error("Could not unzip package" + CodePushUtil.getErrorMessage(unzipError)));
return;
}
try {
const newPackageLocation = LocalPackage.VersionsDir + "/" + this.packageHash;
const deploymentResult = yield LocalPackage.handleDeployment(newPackageLocation);
yield this.verifyPackage(deploymentResult);
this.localPath = deploymentResult.deployDir;
this.finishInstall(deploymentResult.deployDir, installOptions, resolve, installError);
}
catch (error) {
installError(error);
}
}
catch (e) {
installError && installError(new Error("An error occured while installing the package. " + CodePushUtil.getErrorMessage(e)));
}
}));
});
}
verifyPackage(deploymentResult) {
return new Promise((resolve, reject) => {
var deployDir = deploymentResult.deployDir;
var verificationFail = (error) => {
reject(error);
};
var verify = (isSignatureVerificationEnabled, isSignatureAppearedInBundle, publicKey, signature) => {
if (isSignatureVerificationEnabled) {
if (isSignatureAppearedInBundle) {
this.verifyHash(deployDir, this.packageHash, verificationFail, () => {
this.verifySignature(deployDir, this.packageHash, publicKey, signature, verificationFail, resolve);
});
}
else {
var errorMessage = "Error! Public key was provided but there is no JWT signature within app bundle to verify. " +
"Possible reasons, why that might happen: \n" +
"1. You've been released CodePush bundle update using version of CodePush CLI that is not support code signing.\n" +
"2. You've been released CodePush bundle update without providing --privateKeyPath option.";
reject(new Error(errorMessage));
}
}
else {
if (isSignatureAppearedInBundle) {
CodePushUtil.logMessage("Warning! JWT signature exists in codepush update but code integrity check couldn't be performed because there is no public key configured. " +
"Please ensure that public key is properly configured within your application.");
// verifyHash
this.verifyHash(deployDir, this.packageHash, verificationFail, resolve);
}
else {
if (deploymentResult.isDiffUpdate) {
// verifyHash
this.verifyHash(deployDir, this.packageHash, verificationFail, resolve);
}
else {
resolve();
}
}
}
};
if (deploymentResult.isDiffUpdate) {
CodePushUtil.logMessage("Applying diff update");
}
else {
CodePushUtil.logMessage("Applying full update");
}
var isSignatureVerificationEnabled, isSignatureAppearedInBundle;
var publicKey;
this.getPublicKey((error, publicKeyResult) => {
if (error) {
reject(new Error("Error reading public key. " + error));
return;
}
publicKey = publicKeyResult;
isSignatureVerificationEnabled = !!publicKey;
this.getSignatureFromUpdate(deploymentResult.deployDir, (error, signature) => {
if (error) {
reject(new Error("Error reading signature from update. " + error));
return;
}
isSignatureAppearedInBundle = !!signature;
verify(isSignatureVerificationEnabled, isSignatureAppearedInBundle, publicKey, signature);
});
});
});
}
getPublicKey(callback) {
var success = (publicKey) => {
callback(null, publicKey);
};
var fail = (error) => {
callback(error, null);
};
CodePush$1.getPublicKey().then(result => success(result.value || null), fail);
}
getSignatureFromUpdate(deployDir, callback) {
return __awaiter$2(this, void 0, void 0, function* () {
const filePath = deployDir + "/public/.codepushrelease";
if (!(yield FileUtil.fileExists(filesystem.Directory.Data, filePath))) {
// signature absents in the bundle
callback(null, null);
return;
}
try {
const signature = yield FileUtil.readFile(filesystem.Directory.Data, filePath);
callback(null, signature);
}
catch (error) {
// error reading signature file from bundle
callback(error, null);
}
});
}
verifyHash(deployDir, newUpdateHash, errorCallback, successCallback) {
var packageHashSuccess = (computedHash) => {
if (computedHash !== newUpdateHash) {
errorCallback(new Error("The update contents failed the data integrity check."));
return;
}
CodePushUtil.logMessage("The update contents succeeded the data integrity check.");
successCallback();
};
var packageHashFail = (error) => {
errorCallback(new Error("Unable to compute hash for package: " + error));
};
CodePushUtil.logMessage("Verifying hash for folder path: " + deployDir);
CodePush$1.getPackageHash({ path: deployDir }).then(result => packageHashSuccess(result.value), packageHashFail);
}
verifySignature(deployDir, newUpdateHash, publicKey, signature, errorCallback, successCallback) {
var decodeSignatureSuccess = (contentHash) => {
if (contentHash !== newUpdateHash) {
errorCallback(new Error("The update contents failed the code signing check."));
return;
}
CodePushUtil.logMessage("The update contents succeeded the code signing check.");
successCallback();
};
var decodeSignatureFail = (error) => {
errorCallback(new Error("Unable to verify signature for package: " + error));
};
CodePushUtil.logMessage("Verifying signature for folder path: " + deployDir);
CodePush$1.decodeSignature({ publicKey, signature }).then(result => decodeSignatureSuccess(result.value), decodeSignatureFail);
}
finishInstall(deployDir, installOptions, installSuccess, installError) {
function backupPackageInformationFileIfNeeded(backupIfNeededDone) {
return __awaiter$2(this, void 0, void 0, function* () {
const pendingUpdate = yield NativeAppInfo.isPendingUpdate();
if (pendingUpdate) {
// Don't back up the currently installed update since it hasn't been "confirmed"
backupIfNeededDone(null, null);
}
else {
try {
yield LocalPackage.backupPackageInformationFile();
backupIfNeededDone(null, null);
}
catch (err) {
backupIfNeededDone(err, null);
}
}
});
}
LocalPackage.getCurrentOrDefaultPackage().then((oldPackage) => {
backupPackageInformationFileIfNeeded((backupError) => {
/* continue on error, current package information is missing if this is the first update */
this.writeNewPackageMetadata().then(() => {
var invokeSuccessAndInstall = () => {
CodePushUtil.logMessage("Install succeeded.");
var installModeToUse = this.isMandatory ? installOptions.mandatoryInstallMode : installOptions.installMode;
if (installModeToUse === exports.InstallMode.IMMEDIATE) {
/* invoke success before navigating */
installSuccess && installSuccess(installModeToUse);
/* no need for callbacks, the javascript context will reload */
CodePush$1.install({
startLocation: deployDir,
installMode: installModeToUse,
minimumBackgroundDuration: installOptions.minimumBackgroundDuration
});
}
else {
CodePush$1.install({
startLocation: deployDir,
installMode: installModeToUse,
minimumBackgroundDuration: installOptions.minimumBackgroundDuration
}).then(() => { installSuccess && installSuccess(installModeToUse); }, () => { installError && installError(); });
}
};
var preInstallSuccess = () => {
/* package will be cleaned up after success, on the native side */
invokeSuccessAndInstall();
};
var preInstallFailure = (preInstallError) => {
CodePushUtil.logError("Preinstall failure.", preInstallError);
var error = new Error("An error has occured while installing the package. " + CodePushUtil.getErrorMessage(preInstallError));
installError && installError(error);
};
CodePush$1.preInstall({ startLocation: deployDir }).then(preInstallSuccess, preInstallFailure);
}, (writeMetadataError) => {
installError && installError(writeMetadataError);
});
});
}, installError);
}
static handleDeployment(newPackageLocation) {
return __awaiter$2(this, void 0, void 0, function* () {
const manifestFile = {
directory: filesystem.Directory.Data,
path: LocalPackage.DownloadUnzipDir + "/" + LocalPackage.DiffManifestFile
};
const isDiffUpdate = yield FileUtil.fileExists(manifestFile.directory, manifestFile.path);
if (!(yield FileUtil.directoryExists(filesystem.Directory.Data, LocalPackage.VersionsDir))) {
// If directory not exists, create recursive folder
yield filesystem.Filesystem.mkdir({
path: LocalPackage.VersionsDir,
directory: filesystem.Directory.Data,
recursive: true
});
}
if (isDiffUpdate) {
yield LocalPackage.handleDiffDeployment(newPackageLocation, manifestFile);
}
else {
yield LocalPackage.handleCleanDeployment(newPackageLocation);
}
return { deployDir: newPackageLocation, isDiffUpdate };
});
}
writeNewPackageMetadata() {
return __awaiter$2(this, void 0, void 0, function* () {
const timestamp = yield NativeAppInfo.getApplicationBuildTime().catch(buildTimeError => {
CodePushUtil.logError("Could not get application build time. " + buildTimeError);
});
const appVersion = yield NativeAppInfo.getApplicationVersion().catch(appVersionError => {
CodePushUtil.logError("Could not get application version." + appVersionError);
});
const currentPackageMetadata = {
nativeBuildTime: timestamp,
localPath: this.localPath,
appVersion: appVersion,
deploymentKey: this.deploymentKey,
description: this.description,
isMandatory: this.isMandatory,
packageSize: this.packageSize,
label: this.label,
packageHash: this.packageHash,
isFirstRun: false,
failedInstall: false,
install: undefined
};
return new Promise((resolve, reject) => {
LocalPackage.writeCurrentPackageInformation(currentPackageMetadata, error => error ? reject(error) : resolve());
});
});
}
static handleCleanDeployment(newPackageLocation) {
return __awaiter$2(this, void 0, void 0, function* () {
// no diff manifest
const source = { directory: filesystem.Directory.Data, path: LocalPackage.DownloadUnzipDir };
const target = { directory: filesystem.Directory.Data, path: newPackageLocation };
// TODO: create destination directory if it doesn't exist
return FileUtil.copyDirectoryEntriesTo(source, target);
});
}
static copyCurrentPackage(newPackageLocation, ignoreList) {
return __awaiter$2(this, void 0, void 0, function* () {
const currentPackagePath = yield new Promise(resolve => {
LocalPackage.getPackage(LocalPackage.PackageInfoFile, (currentPackage) => resolve(currentPackage.localPath), () => resolve());
});
newPackageLocation = currentPackagePath ? newPackageLocation : newPackageLocation + "/public";
// https://github.com/ionic-team/capacitor/pull/2514 Directory.Application variable was removed. (TODO - for check)
const source = currentPackagePath ? { directory: filesystem.Directory.Data, path: currentPackagePath } : { directory: filesystem.Directory.Data, path: "public" };
const target = { directory: filesystem.Directory.Data, path: newPackageLocation };
return FileUtil.copyDirectoryEntriesTo(source, target, ignoreList);
});
}
static handleDiffDeployment(newPackageLocation, diffManifest) {
return __awaiter$2(this, void 0, void 0, function* () {
let manifest;
try {
yield LocalPackage.copyCurrentPackage(newPackageLocation, [".codepushrelease"]);
yield LocalPackage.handleCleanDeployment(newPackageLocation);
/* delete files mentioned in the manifest */
const content = yield FileUtil.readFile(diffManifest.directory, diffManifest.path);
manifest = JSON.parse(content);
yield FileUtil.deleteEntriesFromDataDirectory(newPackageLocation, manifest.deletedFiles);
}
catch (error) {
throw new Error("Cannot perform diff-update.");
}
});
}
/**
* Writes the given local package information to the current package information file.
* @param packageInfoMetadata The object to serialize.
* @param callback In case of an error, this function will be called with the error as the fist parameter.
*/
static writeCurrentPackageInformation(packageInfoMetadata, callback) {
var content = JSON.stringify(packageInfoMetadata);
FileUtil.writeStringToDataFile(content, LocalPackage.RootDir + "/" + LocalPackage.PackageInfoFile, true, callback);
}
/**
* Backs up the current package information to the old package information file.
* This file is used for recovery in case of an update going wrong.
* @param callback In case of an error, this function will be called with the error as the fist parameter.
*/
static backupPackageInformationFile() {
return __awaiter$2(this, void 0, void 0, function* () {
const source = {
directory: filesystem.Directory.Data,
path: LocalPackage.RootDir + "/" + LocalPackage.PackageInfoFile
};
const destination = {
directory: filesystem.Directory.Data,
path: LocalPackage.RootDir + "/" + LocalPackage.OldPackageInfoFile
};
return FileUtil.copy(source, destination);
});
}
/**
* Get the previous package information.
*
* @param packageSuccess Callback invoked with the old package information.
* @param packageError Optional callback invoked in case of an error.
*/
static getOldPackage(packageSuccess, packageError) {
LocalPackage.getPackage(LocalPackage.OldPackageInfoFile, packageSuccess, packageError);
}
/**
* Reads package information from a given file.
*
* @param packageFile The package file name.
* @param packageSuccess Callback invoked with the package information.
* @param packageError Optional callback invoked in case of an error.
*/
static getPackage(packageFile, packageSuccess, packageError) {
return __awaiter$2(this, void 0, void 0, function* () {
var handleError = (e) => {
packageError && packageError(new Error("Cannot read package information. " + CodePushUtil.getErrorMessage(e)));
};
try {
const content = yield FileUtil.readDataFile(LocalPackage.RootDir + "/" + packageFile);
const packageInfo = JSON.parse(content);
LocalPackage.getLocalPackageFromMetadata(packageInfo).then(packageSuccess, packageError);
}
catch (e) {
handleError(e);
}
});
}
static getLocalPackageFromMetadata(metadata) {
return __awaiter$2(this, void 0, void 0, function* () {
if (!metadata) {
throw new Error("Invalid package metadata.");
}
const installFailed = yield NativeAppInfo.isFailedUpdate(metadata.packageHash);
const isFirstRun = yield NativeAppInfo.isFirstRun(metadata.packageHash);
const localPackage = new LocalPackage();
localPackage.appVersion = metadata.appVersion;
localPackage.deploymentKey = metadata.deploymentKey;
localPackage.description = metadata.description;
localPackage.isMandatory = metadata.isMandatory;
localPackage.failedInstall = installFailed;
localPackage.isFirstRun = isFirstRun;
localPackage.label = metadata.label;
localPackage.localPath = metadata.localPath;
localPackage.packageHash = metadata.packageHash;
localPackage.packageSize = metadata.packageSize;
return localPackage;
});
}
static getCurrentOrDefaultPackage() {
return LocalPackage.getPackageInfoOrDefault(LocalPackage.PackageInfoFile);
}
static getOldOrDefaultPackage() {
return __awaiter$2(this, void 0, void 0, function* () {
return LocalPackage.getPackageInfoOrDefault(LocalPackage.OldPackageInfoFile);
});
}
static getPackageInfoOrDefault(packageFile) {
return __awaiter$2(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
const packageFailure