1
0
mirror of https://gitcode.com/gh_mirrors/re/react-native-pushy.git synced 2025-11-22 23:46:10 +08:00
Code Issues Packages Projects Releases Wiki Activity GitHub Gitee

Compare commits

..

2 Commits

9 changed files with 342 additions and 313 deletions

View File

@@ -60,13 +60,13 @@ export class DownloadTask {
const exists = fileIo.accessSync(params.targetFile); const exists = fileIo.accessSync(params.targetFile);
if (exists) { if (exists) {
await fileIo.unlink(params.targetFile); await fileIo.unlink(params.targetFile);
}else{ } else {
const targetDir = params.targetFile.substring( const targetDir = params.targetFile.substring(
0, 0,
params.targetFile.lastIndexOf('/'), params.targetFile.lastIndexOf('/'),
); );
const exists = fileIo.accessSync(targetDir); const exists = fileIo.accessSync(targetDir);
if(!exists){ if (!exists) {
await fileIo.mkdir(targetDir); await fileIo.mkdir(targetDir);
} }
} }
@@ -83,7 +83,7 @@ export class DownloadTask {
}, },
}); });
if (response.responseCode > 299) { if (response.responseCode > 299) {
throw new Error(`Server error: ${response.responseCode}`); throw Error(`Server error: ${response.responseCode}`);
} }
const contentLength = parseInt(response.header['content-length'] || '0'); const contentLength = parseInt(response.header['content-length'] || '0');
@@ -108,9 +108,10 @@ export class DownloadTask {
const stats = await fileIo.stat(params.targetFile); const stats = await fileIo.stat(params.targetFile);
const fileSize = stats.size; const fileSize = stats.size;
if (fileSize !== contentLength) { if (fileSize !== contentLength) {
throw new Error(`Download incomplete: expected ${contentLength} bytes but got ${stats.size} bytes`); throw Error(
`Download incomplete: expected ${contentLength} bytes but got ${stats.size} bytes`,
);
} }
} catch (error) { } catch (error) {
console.error('Download failed:', error); console.error('Download failed:', error);
throw error; throw error;
@@ -142,7 +143,7 @@ export class DownloadTask {
bytesRead = await fileIo bytesRead = await fileIo
.read(reader.fd, arrayBuffer) .read(reader.fd, arrayBuffer)
.catch((err: BusinessError) => { .catch((err: BusinessError) => {
throw new Error( throw Error(
`Error reading file: ${err.message}, code: ${err.code}`, `Error reading file: ${err.message}, code: ${err.code}`,
); );
}); });
@@ -154,7 +155,7 @@ export class DownloadTask {
length: bytesRead, length: bytesRead,
}) })
.catch((err: BusinessError) => { .catch((err: BusinessError) => {
throw new Error( throw Error(
`Error writing file: ${err.message}, code: ${err.code}`, `Error writing file: ${err.message}, code: ${err.code}`,
); );
}); });
@@ -295,16 +296,16 @@ export class DownloadTask {
} }
} }
if(fn !== '.DS_Store'){ if (fn !== '.DS_Store') {
await zip.decompressFile(fn, params.unzipDirectory); await zip.decompressFile(fn, params.unzipDirectory);
} }
} }
if (!foundDiff) { if (!foundDiff) {
throw new Error('diff.json not found'); throw Error('diff.json not found');
} }
if (!foundBundlePatch) { if (!foundBundlePatch) {
throw new Error('bundle patch not found'); throw Error('bundle patch not found');
} }
await this.copyFromResource(copyList); await this.copyFromResource(copyList);
} }
@@ -366,12 +367,18 @@ export class DownloadTask {
new Uint8Array(entry.content), new Uint8Array(entry.content),
); );
const outputFile = `${params.unzipDirectory}/bundle.harmony.js`; const outputFile = `${params.unzipDirectory}/bundle.harmony.js`;
const writer = await fileIo.open(outputFile, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY); const writer = await fileIo.open(
outputFile,
fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY,
);
const chunkSize = 4096; const chunkSize = 4096;
let bytesWritten = 0; let bytesWritten = 0;
const totalLength = patched.byteLength; const totalLength = patched.byteLength;
while (bytesWritten < totalLength) { while (bytesWritten < totalLength) {
const chunk = patched.slice(bytesWritten, bytesWritten + chunkSize); const chunk = patched.slice(
bytesWritten,
bytesWritten + chunkSize,
);
await fileIo.write(writer.fd, chunk); await fileIo.write(writer.fd, chunk);
bytesWritten += chunk.byteLength; bytesWritten += chunk.byteLength;
} }
@@ -387,10 +394,10 @@ export class DownloadTask {
} }
if (!foundDiff) { if (!foundDiff) {
throw new Error('diff.json not found'); throw Error('diff.json not found');
} }
if (!foundBundlePatch) { if (!foundBundlePatch) {
throw new Error('bundle patch not found'); throw Error('bundle patch not found');
} }
console.info('Patch from PPK completed'); console.info('Patch from PPK completed');
} }
@@ -478,7 +485,7 @@ export class DownloadTask {
await this.downloadFile(params); await this.downloadFile(params);
break; break;
default: default:
throw new Error(`Unknown task type: ${params.type}`); throw Error(`Unknown task type: ${params.type}`);
} }
params.listener?.onDownloadCompleted(params); params.listener?.onDownloadCompleted(params);

View File

@@ -6,249 +6,276 @@ import common from '@ohos.app.ability.common';
import { DownloadTaskParams } from './DownloadTaskParams'; import { DownloadTaskParams } from './DownloadTaskParams';
export class UpdateContext { export class UpdateContext {
private context: common.UIAbilityContext; private context: common.UIAbilityContext;
private rootDir: string; private rootDir: string;
private preferences: preferences.Preferences; private preferences: preferences.Preferences;
private static DEBUG: boolean = false; private static DEBUG: boolean = false;
private static isUsingBundleUrl: boolean = false; private static isUsingBundleUrl: boolean = false;
constructor(context: common.UIAbilityContext) { constructor(context: common.UIAbilityContext) {
this.context = context; this.context = context;
this.rootDir = context.filesDir + '/_update'; this.rootDir = context.filesDir + '/_update';
try { try {
if (!fileIo.accessSync(this.rootDir)) { if (!fileIo.accessSync(this.rootDir)) {
fileIo.mkdirSync(this.rootDir); fileIo.mkdirSync(this.rootDir);
} }
} catch (e) { } catch (e) {
console.error('Failed to create root directory:', e); console.error('Failed to create root directory:', e);
}
this.initPreferences();
} }
this.initPreferences();
}
private initPreferences() { private initPreferences() {
try { try {
this.preferences = preferences.getPreferencesSync(this.context, {name:'update'}); this.preferences = preferences.getPreferencesSync(this.context, {
const packageVersion = this.getPackageVersion(); name: 'update',
const storedVersion = this.preferences.getSync('packageVersion', ''); });
if(!storedVersion){ const packageVersion = this.getPackageVersion();
this.preferences.putSync('packageVersion', packageVersion); const storedVersion = this.preferences.getSync('packageVersion', '');
this.preferences.flush(); if (!storedVersion) {
} else if (storedVersion && packageVersion !== storedVersion) { this.preferences.putSync('packageVersion', packageVersion);
this.preferences.clear(); this.preferences.flush();
this.preferences.putSync('packageVersion', packageVersion); } else if (storedVersion && packageVersion !== storedVersion) {
this.preferences.flush(); this.preferences.clear();
this.cleanUp(); this.preferences.putSync('packageVersion', packageVersion);
}
} catch (e) {
console.error('Failed to init preferences:', e);
}
}
public setKv(key: string, value: string): void {
this.preferences.putSync(key, value);
this.preferences.flush();
}
public getKv(key: string): string {
return this.preferences.getSync(key, '') as string;
}
public isFirstTime(): boolean {
return this.preferences.getSync('firstTime', false) as boolean;
}
public rolledBackVersion(): string {
return this.preferences.getSync('rolledBackVersion', '') as string;
}
public markSuccess(): void {
this.preferences.putSync('firstTimeOk', true);
const lastVersion = this.preferences.getSync('lastVersion', '') as string;
const curVersion = this.preferences.getSync('currentVersion', '') as string;
if (lastVersion && lastVersion !== curVersion) {
this.preferences.deleteSync('lastVersion');
this.preferences.deleteSync(`hash_${lastVersion}`);
}
this.preferences.flush(); this.preferences.flush();
this.cleanUp(); this.cleanUp();
}
} catch (e) {
console.error('Failed to init preferences:', e);
} }
}
public clearFirstTime(): void { public setKv(key: string, value: string): void {
this.preferences.putSync('firstTime', false); this.preferences.putSync(key, value);
this.preferences.flush(); this.preferences.flush();
this.cleanUp(); }
public getKv(key: string): string {
return this.preferences.getSync(key, '') as string;
}
public isFirstTime(): boolean {
return this.preferences.getSync('firstTime', false) as boolean;
}
public rolledBackVersion(): string {
return this.preferences.getSync('rolledBackVersion', '') as string;
}
public markSuccess(): void {
this.preferences.putSync('firstTimeOk', true);
const lastVersion = this.preferences.getSync('lastVersion', '') as string;
const curVersion = this.preferences.getSync('currentVersion', '') as string;
if (lastVersion && lastVersion !== curVersion) {
this.preferences.deleteSync('lastVersion');
this.preferences.deleteSync(`hash_${lastVersion}`);
} }
this.preferences.flush();
this.cleanUp();
}
public clearRollbackMark(): void { public clearFirstTime(): void {
this.preferences.putSync('rolledBackVersion', null); this.preferences.putSync('firstTime', false);
this.preferences.flush(); this.preferences.flush();
this.cleanUp(); this.cleanUp();
}
public clearRollbackMark(): void {
this.preferences.putSync('rolledBackVersion', null);
this.preferences.flush();
this.cleanUp();
}
public async downloadFullUpdate(
url: string,
hash: string,
listener: DownloadFileListener,
): Promise<void> {
try {
const params = new DownloadTaskParams();
params.type = DownloadTaskParams.TASK_TYPE_PATCH_FULL;
params.url = url;
params.hash = hash;
params.listener = listener;
params.targetFile = `${this.rootDir}/${hash}.ppk`;
const downloadTask = new DownloadTask(this.context);
await downloadTask.execute(params);
} catch (e) {
console.error('Failed to download full update:', e);
} }
}
public async downloadFullUpdate(url: string, hash: string, listener: DownloadFileListener): Promise<void> { public async downloadFile(
try { url: string,
const params = new DownloadTaskParams(); hash: string,
params.type = DownloadTaskParams.TASK_TYPE_PATCH_FULL; fileName: string,
params.url = url; listener: DownloadFileListener,
params.hash = hash; ): Promise<void> {
params.listener = listener; const params = new DownloadTaskParams();
params.targetFile = `${this.rootDir}/${hash}.ppk`; params.type = DownloadTaskParams.TASK_TYPE_PLAIN_DOWNLOAD;
const downloadTask = new DownloadTask(this.context); params.url = url;
await downloadTask.execute(params); params.hash = hash;
} catch (e) { params.listener = listener;
console.error('Failed to download full update:', e); params.targetFile = this.rootDir + '/' + fileName;
const downloadTask = new DownloadTask(this.context);
await downloadTask.execute(params);
}
public async downloadPatchFromPpk(
url: string,
hash: string,
originHash: string,
listener: DownloadFileListener,
): Promise<void> {
const params = new DownloadTaskParams();
params.type = DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK;
params.url = url;
params.hash = hash;
params.originHash = originHash;
params.listener = listener;
params.targetFile = `${this.rootDir}/${originHash}_${hash}.ppk.patch`;
params.unzipDirectory = `${this.rootDir}/${hash}`;
params.originDirectory = `${this.rootDir}/${params.originHash}`;
const downloadTask = new DownloadTask(this.context);
await downloadTask.execute(params);
}
public async downloadPatchFromPackage(
url: string,
hash: string,
listener: DownloadFileListener,
): Promise<void> {
try {
const params = new DownloadTaskParams();
params.type = DownloadTaskParams.TASK_TYPE_PATCH_FROM_APP;
params.url = url;
params.hash = hash;
params.listener = listener;
params.targetFile = `${this.rootDir}/${hash}.app.patch`;
params.unzipDirectory = `${this.rootDir}/${hash}`;
const downloadTask = new DownloadTask(this.context);
return await downloadTask.execute(params);
} catch (e) {
throw e;
console.error('Failed to download APK patch:', e);
}
}
public switchVersion(hash: string): void {
try {
const bundlePath = `${this.rootDir}/${hash}/bundle.harmony.js`;
if (!fileIo.accessSync(bundlePath)) {
throw Error(`Bundle version ${hash} not found.`);
}
const lastVersion = this.getKv('currentVersion');
this.setKv('currentVersion', hash);
if (lastVersion && lastVersion !== hash) {
this.setKv('lastVersion', lastVersion);
}
this.setKv('firstTime', 'true');
this.setKv('firstTimeOk', 'false');
this.setKv('rolledBackVersion', '');
} catch (e) {
console.error('Failed to switch version:', e);
}
}
public static getBundleUrl(
context: common.UIAbilityContext,
defaultAssetsUrl?: string,
): string {
return new UpdateContext(context).getBundleUrl(defaultAssetsUrl);
}
public getBundleUrl(defaultAssetsUrl?: string): string {
UpdateContext.isUsingBundleUrl = true;
const currentVersion = this.getCurrentVersion();
if (!currentVersion) {
return defaultAssetsUrl;
}
if (!this.isFirstTime()) {
if (!this.preferences.getSync('firstTimeOk', true)) {
return this.rollBack();
}
}
let version = currentVersion;
while (version) {
const bundleFile = `${this.rootDir}/${version}/bundle.harmony.js`;
try {
if (!fileIo.accessSync(bundleFile)) {
console.error(`Bundle version ${version} not found.`);
version = this.rollBack();
continue;
} }
return bundleFile;
} catch (e) {
console.error('Failed to access bundle file:', e);
version = this.rollBack();
}
} }
return defaultAssetsUrl;
}
public async downloadFile(url: string, hash: string, fileName: string, listener: DownloadFileListener): Promise<void> { getPackageVersion(): string {
const params = new DownloadTaskParams(); let bundleFlags =
params.type = DownloadTaskParams.TASK_TYPE_PLAIN_DOWNLOAD; bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION;
params.url = url; let packageVersion = '';
params.hash = hash; try {
params.listener = listener; const bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleFlags);
params.targetFile = this.rootDir + '/' + fileName; packageVersion = bundleInfo?.versionName || 'Unknown';
} catch (error) {
const downloadTask = new DownloadTask(this.context); console.error('获取包信息失败:', error);
await downloadTask.execute(params);
} }
return packageVersion;
}
public async downloadPatchFromPpk(url: string, hash: string, originHash: string, listener: DownloadFileListener): Promise<void> { public getCurrentVersion(): string {
const params = new DownloadTaskParams(); const currentVersion = this.getKv('currentVersion');
params.type = DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK; return currentVersion;
params.url = url; }
params.hash = hash;
params.originHash = originHash; private rollBack(): string {
params.listener = listener; const lastVersion = this.preferences.getSync('lastVersion', '') as string;
params.targetFile = `${this.rootDir}/${originHash}_${hash}.ppk.patch`; const currentVersion = this.preferences.getSync(
params.unzipDirectory = `${this.rootDir}/${hash}`; 'currentVersion',
params.originDirectory = `${this.rootDir}/${params.originHash}`; '',
) as string;
const downloadTask = new DownloadTask(this.context); if (!lastVersion) {
await downloadTask.execute(params); this.preferences.deleteSync('currentVersion');
} else {
this.preferences.putSync('currentVersion', lastVersion);
} }
this.preferences.putSync('firstTimeOk', true);
this.preferences.putSync('firstTime', false);
this.preferences.putSync('rolledBackVersion', currentVersion);
this.preferences.flush();
return lastVersion;
}
public async downloadPatchFromPackage(url: string, hash: string, listener: DownloadFileListener): Promise<void> { private cleanUp(): void {
try { const params = new DownloadTaskParams();
const params = new DownloadTaskParams(); params.type = DownloadTaskParams.TASK_TYPE_CLEANUP;
params.type = DownloadTaskParams.TASK_TYPE_PATCH_FROM_APP; params.hash = this.preferences.getSync('currentVersion', '') as string;
params.url = url; params.originHash = this.preferences.getSync('lastVersion', '') as string;
params.hash = hash; params.unzipDirectory = this.rootDir;
params.listener = listener; const downloadTask = new DownloadTask(this.context);
params.targetFile = `${this.rootDir}/${hash}.app.patch`; downloadTask.execute(params);
params.unzipDirectory = `${this.rootDir}/${hash}`; }
const downloadTask = new DownloadTask(this.context); public getIsUsingBundleUrl(): boolean {
return await downloadTask.execute(params); return UpdateContext.isUsingBundleUrl;
} catch (e) { }
throw e;
console.error('Failed to download APK patch:', e);
}
}
public switchVersion(hash: string): void {
try {
const bundlePath = `${this.rootDir}/${hash}/bundle.harmony.js`;
if (!fileIo.accessSync(bundlePath)) {
throw new Error(`Bundle version ${hash} not found.`);
}
const lastVersion = this.getKv('currentVersion');
this.setKv('currentVersion', hash);
if (lastVersion && lastVersion !== hash) {
this.setKv('lastVersion', lastVersion);
}
this.setKv('firstTime', 'true');
this.setKv('firstTimeOk', 'false');
this.setKv('rolledBackVersion', "");
} catch (e) {
console.error('Failed to switch version:', e);
}
}
public static getBundleUrl(context: common.UIAbilityContext, defaultAssetsUrl?: string): string {
return new UpdateContext(context).getBundleUrl(defaultAssetsUrl);
}
public getBundleUrl(defaultAssetsUrl?: string): string {
UpdateContext.isUsingBundleUrl = true;
const currentVersion = this.getCurrentVersion();
if (!currentVersion) {
return defaultAssetsUrl;
}
if (!this.isFirstTime()) {
if (!this.preferences.getSync('firstTimeOk', true)) {
return this.rollBack();
}
}
let version = currentVersion;
while (version) {
const bundleFile = `${this.rootDir}/${version}/bundle.harmony.js`;
try {
if (!fileIo.accessSync(bundleFile)) {
console.error(`Bundle version ${version} not found.`);
version = this.rollBack();
continue;
}
return bundleFile;
} catch (e) {
console.error('Failed to access bundle file:', e);
version = this.rollBack();
}
}
return defaultAssetsUrl;
}
getPackageVersion(): string {
let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION;
let packageVersion = '';
try {
const bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleFlags);
packageVersion = bundleInfo?.versionName || "Unknown";
} catch (error) {
console.error("获取包信息失败:", error);
}
return packageVersion;
}
public getCurrentVersion() : string {
const currentVersion = this.getKv('currentVersion');
return currentVersion;
}
private rollBack(): string {
const lastVersion = this.preferences.getSync('lastVersion', '') as string;
const currentVersion = this.preferences.getSync('currentVersion', '') as string;
if (!lastVersion) {
this.preferences.deleteSync('currentVersion');
} else {
this.preferences.putSync('currentVersion', lastVersion);
}
this.preferences.putSync('firstTimeOk', true);
this.preferences.putSync('firstTime', false);
this.preferences.putSync('rolledBackVersion', currentVersion);
this.preferences.flush();
return lastVersion;
}
private cleanUp(): void {
const params = new DownloadTaskParams();
params.type = DownloadTaskParams.TASK_TYPE_CLEANUP;
params.hash = this.preferences.getSync('currentVersion', '') as string;
params.originHash = this.preferences.getSync('lastVersion', '') as string;
params.unzipDirectory = this.rootDir;
const downloadTask = new DownloadTask(this.context);
downloadTask.execute(params);
}
public getIsUsingBundleUrl(): boolean {
return UpdateContext.isUsingBundleUrl;
}
} }
export interface DownloadFileListener { export interface DownloadFileListener {
onDownloadCompleted(params: DownloadTaskParams): void; onDownloadCompleted(params: DownloadTaskParams): void;
onDownloadFailed(error: Error): void; onDownloadFailed(error: Error): void;
} }

View File

@@ -4,14 +4,14 @@ import { UpdateContext } from './UpdateContext';
import { DownloadTaskParams } from './DownloadTaskParams'; import { DownloadTaskParams } from './DownloadTaskParams';
import logger from './Logger'; import logger from './Logger';
const TAG = "UpdateModuleImpl"; const TAG = 'UpdateModuleImpl';
export class UpdateModuleImpl { export class UpdateModuleImpl {
static readonly NAME = "Pushy"; static readonly NAME = 'Pushy';
static async downloadFullUpdate( static async downloadFullUpdate(
updateContext: UpdateContext, updateContext: UpdateContext,
options: { updateUrl: string; hash: string } options: { updateUrl: string; hash: string },
): Promise<void> { ): Promise<void> {
try { try {
await updateContext.downloadFullUpdate(options.updateUrl, options.hash, { await updateContext.downloadFullUpdate(options.updateUrl, options.hash, {
@@ -20,7 +20,7 @@ export class UpdateModuleImpl {
}, },
onDownloadFailed: (error: Error) => { onDownloadFailed: (error: Error) => {
return Promise.reject(error); return Promise.reject(error);
} },
}); });
} catch (error) { } catch (error) {
logger.error(TAG, `downloadFullUpdate failed: ${error}`); logger.error(TAG, `downloadFullUpdate failed: ${error}`);
@@ -30,18 +30,18 @@ export class UpdateModuleImpl {
static async downloadAndInstallApk( static async downloadAndInstallApk(
context: common.UIAbilityContext, context: common.UIAbilityContext,
options: { url: string; hash: string; target: string } options: { url: string; hash: string; target: string },
): Promise<void> { ): Promise<void> {
try { try {
const want = { const want = {
action: 'action.system.home', action: 'action.system.home',
parameters: { parameters: {
uri: 'appmarket://details' uri: 'appmarket://details',
} },
}; };
if (!context) { if (!context) {
throw new Error('获取context失败'); throw Error('获取context失败');
} }
await context.startAbility(want); await context.startAbility(want);
@@ -53,17 +53,21 @@ export class UpdateModuleImpl {
static async downloadPatchFromPackage( static async downloadPatchFromPackage(
updateContext: UpdateContext, updateContext: UpdateContext,
options: { updateUrl: string; hash: string } options: { updateUrl: string; hash: string },
): Promise<void> { ): Promise<void> {
try { try {
return await updateContext.downloadPatchFromPackage(options.updateUrl, options.hash, { return await updateContext.downloadPatchFromPackage(
onDownloadCompleted: (params: DownloadTaskParams) => { options.updateUrl,
return Promise.resolve(); options.hash,
{
onDownloadCompleted: (params: DownloadTaskParams) => {
return Promise.resolve();
},
onDownloadFailed: (error: Error) => {
return Promise.reject(error);
},
}, },
onDownloadFailed: (error: Error) => { );
return Promise.reject(error);
}
});
} catch (error) { } catch (error) {
logger.error(TAG, `downloadPatchFromPackage failed: ${error}`); logger.error(TAG, `downloadPatchFromPackage failed: ${error}`);
throw error; throw error;
@@ -72,7 +76,7 @@ export class UpdateModuleImpl {
static async downloadPatchFromPpk( static async downloadPatchFromPpk(
updateContext: UpdateContext, updateContext: UpdateContext,
options: { updateUrl: string; hash: string; originHash: string } options: { updateUrl: string; hash: string; originHash: string },
): Promise<void> { ): Promise<void> {
try { try {
await updateContext.downloadPatchFromPpk( await updateContext.downloadPatchFromPpk(
@@ -85,49 +89,49 @@ export class UpdateModuleImpl {
}, },
onDownloadFailed: (error: Error) => { onDownloadFailed: (error: Error) => {
return Promise.reject(error); return Promise.reject(error);
} },
} },
); );
} catch (error) { } catch (error) {
logger.error(TAG, `downloadPatchFromPpk failed: ${error}`); logger.error(TAG, `downloadPatchFromPpk failed: ${error}`);
throw new Error(`执行报错: ${error.message}`); throw Error(`执行报错: ${error.message}`);
} }
} }
static async reloadUpdate( static async reloadUpdate(
updateContext: UpdateContext, updateContext: UpdateContext,
context: common.UIAbilityContext, context: common.UIAbilityContext,
options: { hash: string } options: { hash: string },
): Promise<void> { ): Promise<void> {
const hash = options.hash; const hash = options.hash;
if (!hash) { if (!hash) {
throw new Error('hash不能为空'); throw Error('hash不能为空');
} }
try { try {
await updateContext.switchVersion(hash); await updateContext.switchVersion(hash);
const bundleInfo = await bundleManager.getBundleInfoForSelf( const bundleInfo = await bundleManager.getBundleInfoForSelf(
bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION,
); );
await context.terminateSelf(); await context.terminateSelf();
const want = { const want = {
bundleName: bundleInfo.name, bundleName: bundleInfo.name,
abilityName: context.abilityInfo?.name abilityName: context.abilityInfo?.name,
}; };
await context.startAbility(want); await context.startAbility(want);
} catch (error) { } catch (error) {
logger.error(TAG, `reloadUpdate failed: ${error}`); logger.error(TAG, `reloadUpdate failed: ${error}`);
throw new Error(`pushy:switchVersion failed ${error.message}`); throw Error(`pushy:switchVersion failed ${error.message}`);
} }
} }
static async setNeedUpdate( static async setNeedUpdate(
updateContext: UpdateContext, updateContext: UpdateContext,
options: { hash: string } options: { hash: string },
): Promise<boolean> { ): Promise<boolean> {
const hash = options.hash; const hash = options.hash;
if (!hash) { if (!hash) {
throw new Error('hash不能为空'); throw Error('hash不能为空');
} }
try { try {
@@ -135,7 +139,7 @@ export class UpdateModuleImpl {
return true; return true;
} catch (error) { } catch (error) {
logger.error(TAG, `setNeedUpdate failed: ${error}`); logger.error(TAG, `setNeedUpdate failed: ${error}`);
throw new Error(`switchVersionLater failed: ${error.message}`); throw Error(`switchVersionLater failed: ${error.message}`);
} }
} }
@@ -145,20 +149,20 @@ export class UpdateModuleImpl {
return true; return true;
} catch (error) { } catch (error) {
logger.error(TAG, `markSuccess failed: ${error}`); logger.error(TAG, `markSuccess failed: ${error}`);
throw new Error(`执行报错: ${error.message}`); throw Error(`执行报错: ${error.message}`);
} }
} }
static async setUuid( static async setUuid(
updateContext: UpdateContext, updateContext: UpdateContext,
uuid: string uuid: string,
): Promise<boolean> { ): Promise<boolean> {
try { try {
await updateContext.setKv('uuid', uuid); await updateContext.setKv('uuid', uuid);
return true; return true;
} catch (error) { } catch (error) {
logger.error(TAG, `setUuid failed: ${error}`); logger.error(TAG, `setUuid failed: ${error}`);
throw new Error(`执行报错: ${error.message}`); throw Error(`执行报错: ${error.message}`);
} }
} }
@@ -174,17 +178,14 @@ export class UpdateModuleImpl {
static setLocalHashInfo( static setLocalHashInfo(
updateContext: UpdateContext, updateContext: UpdateContext,
hash: string, hash: string,
info: string info: string,
): boolean { ): boolean {
updateContext.setKv(`hash_${hash}`, info); updateContext.setKv(`hash_${hash}`, info);
return true; return true;
} }
static getLocalHashInfo( static getLocalHashInfo(updateContext: UpdateContext, hash: string): string {
updateContext: UpdateContext,
hash: string
): string {
const value = updateContext.getKv(`hash_${hash}`); const value = updateContext.getKv(`hash_${hash}`);
return value; return value;
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-native-update", "name": "react-native-update",
"version": "10.31.3", "version": "10.32.0",
"description": "react-native hot update", "description": "react-native hot update",
"main": "src/index", "main": "src/index",
"scripts": { "scripts": {
@@ -72,6 +72,5 @@
"react-native": "0.73", "react-native": "0.73",
"ts-jest": "^29.3.2", "ts-jest": "^29.3.2",
"typescript": "^5.6.3" "typescript": "^5.6.3"
}, }
"packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72"
} }

View File

@@ -93,7 +93,6 @@ export class Pushy {
clientType: 'Pushy' | 'Cresc' = 'Pushy'; clientType: 'Pushy' | 'Cresc' = 'Pushy';
lastChecking?: number; lastChecking?: number;
lastRespJson?: Promise<CheckResult>; lastRespJson?: Promise<CheckResult>;
lastRespText?: Promise<string>;
version = cInfo.rnu; version = cInfo.rnu;
loggerPromise = (() => { loggerPromise = (() => {
@@ -116,7 +115,7 @@ export class Pushy {
if (Platform.OS === 'ios' || Platform.OS === 'android') { if (Platform.OS === 'ios' || Platform.OS === 'android') {
if (!options.appKey) { if (!options.appKey) {
throw new Error(i18n.t('error_appkey_required')); throw Error(i18n.t('error_appkey_required'));
} }
} }
@@ -293,10 +292,10 @@ export class Pushy {
), ),
); );
} catch (err: any) { } catch (err: any) {
this.throwIfEnabled(new Error('errorCheckingUseBackup')); this.throwIfEnabled(Error('errorCheckingUseBackup'));
} }
} else { } else {
this.throwIfEnabled(new Error('errorCheckingGetBackup')); this.throwIfEnabled(Error('errorCheckingGetBackup'));
} }
} }
if (!resp) { if (!resp) {
@@ -304,21 +303,21 @@ export class Pushy {
type: 'errorChecking', type: 'errorChecking',
message: this.t('error_cannot_connect_server'), message: this.t('error_cannot_connect_server'),
}); });
this.throwIfEnabled(new Error('errorChecking')); this.throwIfEnabled(Error('errorChecking'));
return this.lastRespJson ? await this.lastRespJson : emptyObj; return this.lastRespJson ? await this.lastRespJson : emptyObj;
} }
if (resp.status !== 200) { if (!resp.ok) {
const respText = await resp.text();
const errorMessage = this.t('error_http_status', { const errorMessage = this.t('error_http_status', {
status: resp.status, status: resp.status,
statusText: resp.statusText, statusText: respText,
}); });
this.report({ this.report({
type: 'errorChecking', type: 'errorChecking',
message: errorMessage, message: errorMessage,
}); });
this.throwIfEnabled(new Error(errorMessage)); this.throwIfEnabled(Error(errorMessage));
log('error checking response:', resp.status, await resp.text());
return this.lastRespJson ? await this.lastRespJson : emptyObj; return this.lastRespJson ? await this.lastRespJson : emptyObj;
} }
this.lastRespJson = resp.json(); this.lastRespJson = resp.json();
@@ -435,7 +434,7 @@ export class Pushy {
message: e.message, message: e.message,
}); });
errorMessages.push(errorMessage); errorMessages.push(errorMessage);
lastError = new Error(errorMessage); lastError = Error(errorMessage);
log(errorMessage); log(errorMessage);
} }
} }
@@ -454,7 +453,7 @@ export class Pushy {
message: e.message, message: e.message,
}); });
errorMessages.push(errorMessage); errorMessages.push(errorMessage);
lastError = new Error(errorMessage); lastError = Error(errorMessage);
log(errorMessage); log(errorMessage);
} }
} }
@@ -474,7 +473,7 @@ export class Pushy {
message: e.message, message: e.message,
}); });
errorMessages.push(errorMessage); errorMessages.push(errorMessage);
lastError = new Error(errorMessage); lastError = Error(errorMessage);
log(errorMessage); log(errorMessage);
} }
} else if (__DEV__) { } else if (__DEV__) {
@@ -532,7 +531,7 @@ export class Pushy {
} }
if (sharedState.apkStatus === 'downloaded') { if (sharedState.apkStatus === 'downloaded') {
this.report({ type: 'errorInstallApk' }); this.report({ type: 'errorInstallApk' });
this.throwIfEnabled(new Error('errorInstallApk')); this.throwIfEnabled(Error('errorInstallApk'));
return; return;
} }
if (Platform.Version <= 23) { if (Platform.Version <= 23) {
@@ -542,7 +541,7 @@ export class Pushy {
); );
if (granted !== PermissionsAndroid.RESULTS.GRANTED) { if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
this.report({ type: 'rejectStoragePermission' }); this.report({ type: 'rejectStoragePermission' });
this.throwIfEnabled(new Error('rejectStoragePermission')); this.throwIfEnabled(Error('rejectStoragePermission'));
return; return;
} }
} catch (e: any) { } catch (e: any) {
@@ -575,7 +574,7 @@ export class Pushy {
}).catch(() => { }).catch(() => {
sharedState.apkStatus = null; sharedState.apkStatus = null;
this.report({ type: 'errorDownloadAndInstallApk' }); this.report({ type: 'errorDownloadAndInstallApk' });
this.throwIfEnabled(new Error('errorDownloadAndInstallApk')); this.throwIfEnabled(Error('errorDownloadAndInstallApk'));
}); });
sharedState.apkStatus = 'downloaded'; sharedState.apkStatus = 'downloaded';
if (sharedState.progressHandlers[progressKey]) { if (sharedState.progressHandlers[progressKey]) {

View File

@@ -18,7 +18,7 @@ export const PushyModule =
export const UpdateModule = PushyModule; export const UpdateModule = PushyModule;
if (!PushyModule) { if (!PushyModule) {
throw new Error( throw Error(
'Failed to load react-native-update native module, please try to recompile', 'Failed to load react-native-update native module, please try to recompile',
); );
} }

View File

@@ -16,9 +16,9 @@ import { Pushy, Cresc, sharedState } from './client';
import { currentVersion, packageVersion, getCurrentVersionInfo, currentVersionInfo } from './core'; import { currentVersion, packageVersion, getCurrentVersionInfo, currentVersionInfo } from './core';
import { import {
CheckResult, CheckResult,
MixedCheckResult,
ProgressData, ProgressData,
UpdateTestPayload, UpdateTestPayload,
VersionInfo,
} from './type'; } from './type';
import { UpdateContext } from './context'; import { UpdateContext } from './context';
import { URL } from 'react-native-url-polyfill'; import { URL } from 'react-native-url-polyfill';
@@ -163,7 +163,7 @@ export const UpdateProvider = ({
return; return;
} }
lastChecking.current = now; lastChecking.current = now;
let rootInfo: MixedCheckResult | undefined; let rootInfo: CheckResult | undefined;
try { try {
rootInfo = await client.checkUpdate(extra); rootInfo = await client.checkUpdate(extra);
} catch (e: any) { } catch (e: any) {
@@ -175,8 +175,8 @@ export const UpdateProvider = ({
if (!rootInfo) { if (!rootInfo) {
return; return;
} }
const versions = rootInfo.versions || [rootInfo as CheckResult]; const versions = [rootInfo.expVersion, rootInfo].filter(Boolean) as VersionInfo[];
delete rootInfo.versions; delete rootInfo.expVersion;
for (const versionInfo of versions) { for (const versionInfo of versions) {
const info: CheckResult = { const info: CheckResult = {
...versionInfo, ...versionInfo,
@@ -242,7 +242,7 @@ export const UpdateProvider = ({
alertUpdate( alertUpdate(
client.t('alert_title'), client.t('alert_title'),
client.t('alert_new_version_found', { client.t('alert_new_version_found', {
name: info.name, name: info.name!,
description: info.description, description: info.description,
}), }),
[ [

View File

@@ -24,14 +24,10 @@ interface RootResult {
paths?: string[]; paths?: string[];
} }
export type CheckResult = RootResult & VersionInfo; export type CheckResult = RootResult &
Partial<VersionInfo> & {
export type CheckResultV2 = RootResult & { expVersion?: VersionInfo;
versions?: VersionInfo[]; };
};
export type MixedCheckResult = CheckResult | CheckResultV2;
export interface ProgressData { export interface ProgressData {
hash: string; hash: string;

View File

@@ -17,7 +17,7 @@ export function promiseAny<T>(promises: Promise<T>[]) {
.catch(() => { .catch(() => {
count++; count++;
if (count === promises.length) { if (count === promises.length) {
reject(new Error(i18n.t('error_all_promises_rejected'))); reject(Error(i18n.t('error_all_promises_rejected')));
} }
}); });
}); });
@@ -51,7 +51,7 @@ const ping = isWeb
return finalUrl; return finalUrl;
} }
log('ping failed', url, status, statusText); log('ping failed', url, status, statusText);
throw new Error(i18n.t('error_ping_failed')); throw Error(i18n.t('error_ping_failed'));
}) })
.catch(e => { .catch(e => {
pingFinished = true; pingFinished = true;
@@ -60,7 +60,7 @@ const ping = isWeb
}), }),
new Promise((_, reject) => new Promise((_, reject) =>
setTimeout(() => { setTimeout(() => {
reject(new Error(i18n.t('error_ping_timeout'))); reject(Error(i18n.t('error_ping_timeout')));
if (!pingFinished) { if (!pingFinished) {
log('ping timeout', url); log('ping timeout', url);
} }
@@ -115,7 +115,7 @@ export const enhancedFetch = async (
if (r.ok) { if (r.ok) {
return r; return r;
} }
throw new Error( throw Error(
i18n.t('error_http_status', { i18n.t('error_http_status', {
status: r.status, status: r.status,
statusText: r.statusText, statusText: r.statusText,