react-native-update 10.59.0 → 10.59.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +46 -14
- package/android/jni/Android.mk +2 -1
- package/android/lib/arm64-v8a/librnpushy.so +0 -0
- package/android/lib/armeabi-v7a/librnpushy.so +0 -0
- package/android/lib/x86/librnpushy.so +0 -0
- package/android/lib/x86_64/librnpushy.so +0 -0
- package/android/src/main/java/cn/reactnative/modules/update/ApkInstaller.java +6 -6
- package/android/src/main/java/cn/reactnative/modules/update/BundledResourceCopier.java +4 -4
- package/android/src/main/java/cn/reactnative/modules/update/{CrashRescue.java → CrashHold.java} +12 -12
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +28 -24
- package/android/src/main/java/cn/reactnative/modules/update/ErrorCodes.java +2 -1
- package/android/src/main/java/cn/reactnative/modules/update/{NativeUpdateFlow.java → FlowBridge.java} +6 -6
- package/android/src/main/java/cn/reactnative/modules/update/HttpUtils.java +6 -3
- package/android/src/main/java/cn/reactnative/modules/update/{NativeUpdateCore.java → NativeCore.java} +4 -4
- package/android/src/main/java/cn/reactnative/modules/update/PushyConfiguration.java +10 -5
- package/android/src/main/java/cn/reactnative/modules/update/PushyRuntime.java +4 -4
- package/android/src/main/java/cn/reactnative/modules/update/ReactReloadManager.java +7 -7
- package/android/src/main/java/cn/reactnative/modules/update/StateSerialRunner.java +1 -1
- package/android/src/main/java/cn/reactnative/modules/update/{NativeCheckOrchestrator.java → SyncCoordinator.java} +79 -75
- package/android/src/main/java/cn/reactnative/modules/update/Texts.java +28 -0
- package/android/src/main/java/cn/reactnative/modules/update/UiThreadRunner.java +1 -1
- package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +28 -28
- package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleImpl.java +12 -9
- package/cpp/patch_core/archive_limits.h +5 -0
- package/cpp/patch_core/archive_patch_core.cpp +26 -19
- package/cpp/patch_core/archive_patch_core.h +17 -8
- package/cpp/patch_core/digest.h +6 -1
- package/cpp/patch_core/error_codes.h +8 -1
- package/cpp/patch_core/hbc_transform.h +5 -0
- package/cpp/patch_core/hbc_transform_wire.h +5 -0
- package/cpp/patch_core/install_record.h +5 -0
- package/cpp/patch_core/jni_natives.h +27 -0
- package/cpp/patch_core/jni_registration.cpp +103 -0
- package/cpp/patch_core/obscured_text.h +44 -0
- package/cpp/patch_core/patch_core.cpp +13 -13
- package/cpp/patch_core/patch_core.h +11 -6
- package/cpp/patch_core/patch_core_android.cpp +10 -10
- package/cpp/patch_core/state_core.h +5 -0
- package/cpp/patch_core/state_ops.h +5 -0
- package/cpp/patch_core/update_core_android.cpp +23 -22
- package/cpp/update_flow_core/flow_json.h +6 -1
- package/cpp/update_flow_core/update_flow_core.cpp +35 -18
- package/cpp/update_flow_core/update_flow_core.h +13 -8
- package/cpp/update_flow_core/update_flow_jni.cpp +11 -13
- package/harmony/pushy/src/main/cpp/CMakeLists.txt +8 -8
- package/harmony/pushy/src/main/cpp/pushy.cpp +33 -33
- package/harmony/pushy/src/main/ets/ArchiveLimits.ts +15 -15
- package/harmony/pushy/src/main/ets/DownloadTask.ts +60 -60
- package/harmony/pushy/src/main/ets/ErrorCodes.ts +13 -11
- package/harmony/pushy/src/main/ets/InstallRecord.ts +4 -4
- package/harmony/pushy/src/main/ets/NativePatchCore.ts +9 -9
- package/harmony/pushy/src/main/ets/PathUtils.ts +2 -2
- package/harmony/pushy/src/main/ets/PushyConfiguration.ts +8 -6
- package/harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets +1 -1
- package/harmony/pushy/src/main/ets/PushyTurboModule.ts +42 -41
- package/harmony/pushy/src/main/ets/{NativeCheckOrchestrator.ts → SyncCoordinator.ts} +33 -32
- package/harmony/pushy/src/main/ets/Texts.ts +28 -0
- package/harmony/pushy/src/main/ets/UpdateContext.ts +31 -30
- package/harmony/pushy.har +0 -0
- package/ios/RCTPushy/RCTPushy.mm +148 -140
- package/ios/RCTPushy/RCTPushyConfiguration.h +8 -0
- package/ios/RCTPushy/RCTPushyConfiguration.mm +32 -5
- package/package.json +1 -1
- package/react-native-update.podspec +15 -3
- package/android/lib/arm64-v8a/librnupdate.so +0 -0
- package/android/lib/armeabi-v7a/librnupdate.so +0 -0
- package/android/lib/x86/librnupdate.so +0 -0
- package/android/lib/x86_64/librnupdate.so +0 -0
|
@@ -31,11 +31,12 @@ import {
|
|
|
31
31
|
ERROR_DOWNLOAD_FAILED,
|
|
32
32
|
ERROR_FILE_OPERATION_FAILED,
|
|
33
33
|
ERROR_INVALID_OPTIONS,
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
ERROR_DELTA_FAILED,
|
|
35
|
+
createPushyError,
|
|
36
36
|
getErrorMessage,
|
|
37
|
-
|
|
37
|
+
toPushyError,
|
|
38
38
|
} from './ErrorCodes';
|
|
39
|
+
import { BUNDLE_DELTA_ENTRY } from './Texts';
|
|
39
40
|
|
|
40
41
|
const TAG = 'DownloadTask';
|
|
41
42
|
|
|
@@ -92,7 +93,7 @@ export function parseManifestToArrays(
|
|
|
92
93
|
| undefined;
|
|
93
94
|
const hbcTransformEntry =
|
|
94
95
|
hbcTransform && typeof hbcTransform === 'object'
|
|
95
|
-
? hbcTransform[
|
|
96
|
+
? hbcTransform[BUNDLE_DELTA_ENTRY]
|
|
96
97
|
: undefined;
|
|
97
98
|
const hbcTransformMeta =
|
|
98
99
|
hbcTransformEntry && typeof hbcTransformEntry === 'object'
|
|
@@ -108,7 +109,7 @@ export function parseManifestToArrays(
|
|
|
108
109
|
};
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
interface
|
|
112
|
+
interface DeltaInputs {
|
|
112
113
|
entryNames: string[];
|
|
113
114
|
manifestArrays: PatchManifestArrays;
|
|
114
115
|
}
|
|
@@ -125,7 +126,6 @@ function toArrayBufferSlice(
|
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
const DIFF_MANIFEST_ENTRY = '__diff.json';
|
|
128
|
-
const HARMONY_BUNDLE_PATCH_ENTRY = 'bundle.harmony.js.patch';
|
|
129
129
|
const TEMP_ORIGIN_BUNDLE_ENTRY = '.origin.bundle.harmony.js';
|
|
130
130
|
const FILE_COPY_BUFFER_SIZE = 64 * 1024;
|
|
131
131
|
const DOWNLOAD_CALL_TIMEOUT_MS = 10 * 60 * 1000;
|
|
@@ -196,8 +196,8 @@ export class DownloadTask {
|
|
|
196
196
|
const work = this.stagingDirectory(params);
|
|
197
197
|
const bundlePath = `${work}/${HARMONY_BUNDLE_FILE_NAME}`;
|
|
198
198
|
if (!fileIo.accessSync(bundlePath)) {
|
|
199
|
-
throw
|
|
200
|
-
|
|
199
|
+
throw createPushyError(
|
|
200
|
+
ERROR_DELTA_FAILED,
|
|
201
201
|
`bundle missing after install: ${bundlePath}`,
|
|
202
202
|
);
|
|
203
203
|
}
|
|
@@ -293,7 +293,7 @@ export class DownloadTask {
|
|
|
293
293
|
}
|
|
294
294
|
} catch (error) {
|
|
295
295
|
logger.error(TAG, `Failed to delete ${path}: ${getErrorMessage(error)}`);
|
|
296
|
-
throw
|
|
296
|
+
throw toPushyError(error, ERROR_FILE_OPERATION_FAILED);
|
|
297
297
|
}
|
|
298
298
|
}
|
|
299
299
|
|
|
@@ -337,14 +337,14 @@ export class DownloadTask {
|
|
|
337
337
|
try {
|
|
338
338
|
size = await zlib.getOriginalSize(archiveFile);
|
|
339
339
|
} catch (e) {
|
|
340
|
-
throw
|
|
341
|
-
|
|
340
|
+
throw createPushyError(
|
|
341
|
+
ERROR_DELTA_FAILED,
|
|
342
342
|
`cannot determine archive expansion size: ${getErrorMessage(e)}`,
|
|
343
343
|
);
|
|
344
344
|
}
|
|
345
345
|
if (typeof size !== 'number' || !Number.isFinite(size) || size < 0) {
|
|
346
|
-
throw
|
|
347
|
-
|
|
346
|
+
throw createPushyError(
|
|
347
|
+
ERROR_DELTA_FAILED,
|
|
348
348
|
`cannot determine archive expansion size: ${String(size)}`,
|
|
349
349
|
);
|
|
350
350
|
}
|
|
@@ -364,8 +364,8 @@ export class DownloadTask {
|
|
|
364
364
|
): Promise<void> {
|
|
365
365
|
const archiveStat = await fileIo.stat(archiveFile);
|
|
366
366
|
if (archiveStat.size > MAX_ARCHIVE_BYTES) {
|
|
367
|
-
throw
|
|
368
|
-
|
|
367
|
+
throw createPushyError(
|
|
368
|
+
ERROR_DELTA_FAILED,
|
|
369
369
|
`archive too large: ${archiveStat.size} bytes`,
|
|
370
370
|
);
|
|
371
371
|
}
|
|
@@ -375,7 +375,7 @@ export class DownloadTask {
|
|
|
375
375
|
try {
|
|
376
376
|
await zlib.decompressFile(archiveFile, unzipDirectory);
|
|
377
377
|
} catch (e) {
|
|
378
|
-
throw
|
|
378
|
+
throw toPushyError(e, ERROR_DELTA_FAILED);
|
|
379
379
|
}
|
|
380
380
|
await measureExtractedDirectory(unzipDirectory);
|
|
381
381
|
}
|
|
@@ -468,9 +468,9 @@ export class DownloadTask {
|
|
|
468
468
|
|
|
469
469
|
const manifestStat = await fileIo.stat(manifestPath);
|
|
470
470
|
if (manifestStat.size > MAX_MANIFEST_BYTES) {
|
|
471
|
-
throw
|
|
472
|
-
|
|
473
|
-
`
|
|
471
|
+
throw createPushyError(
|
|
472
|
+
ERROR_DELTA_FAILED,
|
|
473
|
+
`delta manifest too large: ${manifestStat.size} bytes`,
|
|
474
474
|
);
|
|
475
475
|
}
|
|
476
476
|
return parseManifestToArrays(
|
|
@@ -483,29 +483,29 @@ export class DownloadTask {
|
|
|
483
483
|
private async applyBundlePatchFromFileSource(
|
|
484
484
|
originContent: ArrayBuffer,
|
|
485
485
|
workingDirectory: string,
|
|
486
|
-
|
|
486
|
+
bundleDeltaPath: string,
|
|
487
487
|
outputFile: string,
|
|
488
488
|
hbcTransformMeta = '',
|
|
489
489
|
): Promise<void> {
|
|
490
490
|
const originBundlePath = `${workingDirectory}/${TEMP_ORIGIN_BUNDLE_ENTRY}`;
|
|
491
491
|
try {
|
|
492
492
|
await this.writeFileContent(originBundlePath, originContent);
|
|
493
|
-
await NativePatchCore.
|
|
493
|
+
await NativePatchCore.applyDeltaFromSource({
|
|
494
494
|
copyFroms: [],
|
|
495
495
|
copyTos: [],
|
|
496
496
|
deletes: [],
|
|
497
497
|
sourceRoot: workingDirectory,
|
|
498
498
|
targetRoot: workingDirectory,
|
|
499
499
|
originBundlePath,
|
|
500
|
-
|
|
500
|
+
bundleDeltaPath,
|
|
501
501
|
bundleOutputPath: outputFile,
|
|
502
502
|
enableMerge: false,
|
|
503
503
|
bundleHbcTransformMeta: hbcTransformMeta,
|
|
504
504
|
});
|
|
505
505
|
} catch (error) {
|
|
506
|
-
throw
|
|
507
|
-
|
|
508
|
-
`Failed to process bundle
|
|
506
|
+
throw createPushyError(
|
|
507
|
+
ERROR_DELTA_FAILED,
|
|
508
|
+
`Failed to process bundle delta: ${getErrorMessage(error)}`,
|
|
509
509
|
);
|
|
510
510
|
} finally {
|
|
511
511
|
if (fileIo.accessSync(originBundlePath)) {
|
|
@@ -570,7 +570,7 @@ export class DownloadTask {
|
|
|
570
570
|
await this.deleteArchiveAndSidecar(params.targetFile);
|
|
571
571
|
const retry = await this.transferArchive(params, false);
|
|
572
572
|
if (retry !== 'done') {
|
|
573
|
-
throw
|
|
573
|
+
throw createPushyError(
|
|
574
574
|
ERROR_DOWNLOAD_FAILED,
|
|
575
575
|
`Server rejected the download range for ${params.url}`,
|
|
576
576
|
);
|
|
@@ -607,7 +607,7 @@ export class DownloadTask {
|
|
|
607
607
|
? params.deadlineUptimeMs
|
|
608
608
|
: monotonicNowMs() + DOWNLOAD_CALL_TIMEOUT_MS;
|
|
609
609
|
if (deadlineUptimeMs <= monotonicNowMs()) {
|
|
610
|
-
throw
|
|
610
|
+
throw createPushyError(
|
|
611
611
|
ERROR_DOWNLOAD_FAILED,
|
|
612
612
|
'Download deadline expired before start',
|
|
613
613
|
);
|
|
@@ -684,8 +684,8 @@ export class DownloadTask {
|
|
|
684
684
|
received += data.byteLength;
|
|
685
685
|
if (!writeError && baseOffset + received > MAX_ARCHIVE_BYTES) {
|
|
686
686
|
// 未知长度/分块传输的兜底:超过上限即停写,请求在下面结算时失败。
|
|
687
|
-
writeError =
|
|
688
|
-
|
|
687
|
+
writeError = createPushyError(
|
|
688
|
+
ERROR_DELTA_FAILED,
|
|
689
689
|
`archive too large: exceeded ${MAX_ARCHIVE_BYTES}`,
|
|
690
690
|
);
|
|
691
691
|
}
|
|
@@ -735,7 +735,7 @@ export class DownloadTask {
|
|
|
735
735
|
watchdogTimer = setTimeout(() => {
|
|
736
736
|
if (inactivityReject) {
|
|
737
737
|
inactivityReject(
|
|
738
|
-
|
|
738
|
+
createPushyError(
|
|
739
739
|
ERROR_DOWNLOAD_FAILED,
|
|
740
740
|
`Download stalled: no data received for ${INACTIVITY_TIMEOUT_MS}ms`,
|
|
741
741
|
),
|
|
@@ -855,7 +855,7 @@ export class DownloadTask {
|
|
|
855
855
|
const deadlinePromise = new Promise<never>((_, reject) => {
|
|
856
856
|
deadlineTimer = setTimeout(() => {
|
|
857
857
|
reject(
|
|
858
|
-
|
|
858
|
+
createPushyError(
|
|
859
859
|
ERROR_DOWNLOAD_FAILED,
|
|
860
860
|
'Download exceeded its whole-call deadline',
|
|
861
861
|
),
|
|
@@ -894,7 +894,7 @@ export class DownloadTask {
|
|
|
894
894
|
// 响应体从未落盘,partial + sidecar 原样保留——那就是续传状态。
|
|
895
895
|
discardBody = true;
|
|
896
896
|
pendingChunks = null;
|
|
897
|
-
throw
|
|
897
|
+
throw createPushyError(
|
|
898
898
|
ERROR_DOWNLOAD_FAILED,
|
|
899
899
|
`Server error: ${responseCode}`,
|
|
900
900
|
);
|
|
@@ -945,8 +945,8 @@ export class DownloadTask {
|
|
|
945
945
|
);
|
|
946
946
|
}
|
|
947
947
|
if (totalAll > MAX_ARCHIVE_BYTES) {
|
|
948
|
-
writeError =
|
|
949
|
-
|
|
948
|
+
writeError = createPushyError(
|
|
949
|
+
ERROR_DELTA_FAILED,
|
|
950
950
|
`archive too large: ${totalAll} bytes`,
|
|
951
951
|
);
|
|
952
952
|
throw writeError;
|
|
@@ -997,13 +997,13 @@ export class DownloadTask {
|
|
|
997
997
|
const stats = await fileIo.stat(params.targetFile);
|
|
998
998
|
if (!finalEncoded) {
|
|
999
999
|
if (contentLength > 0 && received !== contentLength) {
|
|
1000
|
-
throw
|
|
1000
|
+
throw createPushyError(
|
|
1001
1001
|
ERROR_DOWNLOAD_FAILED,
|
|
1002
1002
|
`Download incomplete: expected ${contentLength} bytes but got ${received} bytes`,
|
|
1003
1003
|
);
|
|
1004
1004
|
}
|
|
1005
1005
|
if (totalAll > 0 && stats.size !== totalAll) {
|
|
1006
|
-
throw
|
|
1006
|
+
throw createPushyError(
|
|
1007
1007
|
ERROR_DOWNLOAD_FAILED,
|
|
1008
1008
|
`Download incomplete: expected ${totalAll} total bytes but got ${stats.size} bytes`,
|
|
1009
1009
|
);
|
|
@@ -1026,7 +1026,7 @@ export class DownloadTask {
|
|
|
1026
1026
|
return 'done';
|
|
1027
1027
|
} catch (error) {
|
|
1028
1028
|
logger.error(TAG, `Download failed: ${getErrorMessage(error)}`);
|
|
1029
|
-
throw
|
|
1029
|
+
throw toPushyError(error, ERROR_DOWNLOAD_FAILED);
|
|
1030
1030
|
} finally {
|
|
1031
1031
|
clearWatchdog();
|
|
1032
1032
|
if (deadlineTimer !== null) {
|
|
@@ -1075,10 +1075,10 @@ export class DownloadTask {
|
|
|
1075
1075
|
}
|
|
1076
1076
|
|
|
1077
1077
|
/** 解压目录里的条目名 + __diff.json 解析结果,供 patch plan 使用。 */
|
|
1078
|
-
private async
|
|
1078
|
+
private async readDeltaInputs(
|
|
1079
1079
|
work: string,
|
|
1080
1080
|
normalizeResourceCopies: boolean,
|
|
1081
|
-
): Promise<
|
|
1081
|
+
): Promise<DeltaInputs> {
|
|
1082
1082
|
const results = await Promise.all([
|
|
1083
1083
|
this.listEntryNames(work),
|
|
1084
1084
|
this.readManifestArrays(work, normalizeResourceCopies),
|
|
@@ -1093,21 +1093,21 @@ export class DownloadTask {
|
|
|
1093
1093
|
|
|
1094
1094
|
private async doPatchFromApp(params: DownloadTaskParams): Promise<void> {
|
|
1095
1095
|
const work = await this.downloadAndExtract(params);
|
|
1096
|
-
const inputs = await this.
|
|
1096
|
+
const inputs = await this.readDeltaInputs(work, true);
|
|
1097
1097
|
const manifestArrays = inputs.manifestArrays;
|
|
1098
1098
|
|
|
1099
|
-
NativePatchCore.
|
|
1099
|
+
NativePatchCore.buildArchivePlan(
|
|
1100
1100
|
ARCHIVE_PATCH_TYPE_FROM_PACKAGE,
|
|
1101
1101
|
inputs.entryNames,
|
|
1102
1102
|
manifestArrays.copyFroms,
|
|
1103
1103
|
manifestArrays.copyTos,
|
|
1104
1104
|
manifestArrays.deletes,
|
|
1105
|
-
|
|
1105
|
+
BUNDLE_DELTA_ENTRY,
|
|
1106
1106
|
);
|
|
1107
1107
|
|
|
1108
|
-
const
|
|
1109
|
-
if (!fileIo.accessSync(
|
|
1110
|
-
throw
|
|
1108
|
+
const bundleDeltaPath = `${work}/${BUNDLE_DELTA_ENTRY}`;
|
|
1109
|
+
if (!fileIo.accessSync(bundleDeltaPath)) {
|
|
1110
|
+
throw createPushyError(ERROR_DELTA_FAILED, 'bundle delta not found');
|
|
1111
1111
|
}
|
|
1112
1112
|
const resourceManager = this.context.resourceManager;
|
|
1113
1113
|
const originContent = await resourceManager.getRawFileContent(
|
|
@@ -1116,7 +1116,7 @@ export class DownloadTask {
|
|
|
1116
1116
|
await this.applyBundlePatchFromFileSource(
|
|
1117
1117
|
originContent.buffer as ArrayBuffer,
|
|
1118
1118
|
work,
|
|
1119
|
-
|
|
1119
|
+
bundleDeltaPath,
|
|
1120
1120
|
`${work}/${HARMONY_BUNDLE_FILE_NAME}`,
|
|
1121
1121
|
manifestArrays.hbcTransformMeta,
|
|
1122
1122
|
);
|
|
@@ -1142,31 +1142,31 @@ export class DownloadTask {
|
|
|
1142
1142
|
|
|
1143
1143
|
private async doPatchFromPpk(params: DownloadTaskParams): Promise<void> {
|
|
1144
1144
|
const work = await this.downloadAndExtract(params);
|
|
1145
|
-
const inputs = await this.
|
|
1145
|
+
const inputs = await this.readDeltaInputs(work, false);
|
|
1146
1146
|
const manifestArrays = inputs.manifestArrays;
|
|
1147
1147
|
|
|
1148
|
-
const plan = NativePatchCore.
|
|
1148
|
+
const plan = NativePatchCore.buildArchivePlan(
|
|
1149
1149
|
ARCHIVE_PATCH_TYPE_FROM_PPK,
|
|
1150
1150
|
inputs.entryNames,
|
|
1151
1151
|
manifestArrays.copyFroms,
|
|
1152
1152
|
manifestArrays.copyTos,
|
|
1153
1153
|
manifestArrays.deletes,
|
|
1154
|
-
|
|
1154
|
+
BUNDLE_DELTA_ENTRY,
|
|
1155
1155
|
);
|
|
1156
|
-
await NativePatchCore.
|
|
1156
|
+
await NativePatchCore.applyDeltaFromSource({
|
|
1157
1157
|
copyFroms: manifestArrays.copyFroms,
|
|
1158
1158
|
copyTos: manifestArrays.copyTos,
|
|
1159
1159
|
deletes: manifestArrays.deletes,
|
|
1160
1160
|
sourceRoot: params.originDirectory,
|
|
1161
1161
|
targetRoot: work,
|
|
1162
1162
|
originBundlePath: `${params.originDirectory}/bundle.harmony.js`,
|
|
1163
|
-
|
|
1163
|
+
bundleDeltaPath: `${work}/${BUNDLE_DELTA_ENTRY}`,
|
|
1164
1164
|
bundleOutputPath: `${work}/${HARMONY_BUNDLE_FILE_NAME}`,
|
|
1165
1165
|
mergeSourceSubdir: plan.mergeSourceSubdir,
|
|
1166
1166
|
enableMerge: plan.enableMerge,
|
|
1167
1167
|
bundleHbcTransformMeta: manifestArrays.hbcTransformMeta,
|
|
1168
1168
|
});
|
|
1169
|
-
logger.info(TAG, '
|
|
1169
|
+
logger.info(TAG, 'Delta from PPK completed');
|
|
1170
1170
|
await this.deleteArchiveAndSidecar(params.targetFile);
|
|
1171
1171
|
}
|
|
1172
1172
|
|
|
@@ -1242,10 +1242,10 @@ export class DownloadTask {
|
|
|
1242
1242
|
}
|
|
1243
1243
|
}
|
|
1244
1244
|
} catch (error) {
|
|
1245
|
-
const coded =
|
|
1245
|
+
const coded = toPushyError(error, ERROR_DELTA_FAILED);
|
|
1246
1246
|
const message = `Copy from resource failed: ${currentFrom}, ${getErrorMessage(error)}`;
|
|
1247
1247
|
logger.error(TAG, message);
|
|
1248
|
-
throw
|
|
1248
|
+
throw createPushyError(coded.code, message);
|
|
1249
1249
|
}
|
|
1250
1250
|
}
|
|
1251
1251
|
|
|
@@ -1275,8 +1275,8 @@ export class DownloadTask {
|
|
|
1275
1275
|
}
|
|
1276
1276
|
const actualCrc = NativePatchCore.crc32(content);
|
|
1277
1277
|
if (actualCrc !== expectedCrc) {
|
|
1278
|
-
throw
|
|
1279
|
-
|
|
1278
|
+
throw createPushyError(
|
|
1279
|
+
ERROR_DELTA_FAILED,
|
|
1280
1280
|
`resource content mismatch (crc32): ${from}`,
|
|
1281
1281
|
);
|
|
1282
1282
|
}
|
|
@@ -1292,7 +1292,7 @@ export class DownloadTask {
|
|
|
1292
1292
|
} catch (error) {
|
|
1293
1293
|
const message = `Cleanup failed: ${getErrorMessage(error)}`;
|
|
1294
1294
|
logger.error(TAG, message);
|
|
1295
|
-
throw
|
|
1295
|
+
throw createPushyError(ERROR_FILE_OPERATION_FAILED, message);
|
|
1296
1296
|
}
|
|
1297
1297
|
}
|
|
1298
1298
|
|
|
@@ -1322,7 +1322,7 @@ export class DownloadTask {
|
|
|
1322
1322
|
await this.deleteResumeSidecar(params.targetFile);
|
|
1323
1323
|
break;
|
|
1324
1324
|
default:
|
|
1325
|
-
throw
|
|
1325
|
+
throw createPushyError(
|
|
1326
1326
|
ERROR_INVALID_OPTIONS,
|
|
1327
1327
|
`Unknown task type: ${params.type}`,
|
|
1328
1328
|
);
|
|
@@ -1335,9 +1335,9 @@ export class DownloadTask {
|
|
|
1335
1335
|
// (解压/hpatch/资源拷贝含 copiesCrc 校验/完成记录)一律 PATCH_FAILED,
|
|
1336
1336
|
// JS 层 patch-health 遥测据此区分网络失败与补丁失败。已带码的错误
|
|
1337
1337
|
// (磁盘空间、参数)原样保留。
|
|
1338
|
-
const error =
|
|
1338
|
+
const error = toPushyError(
|
|
1339
1339
|
rawError,
|
|
1340
|
-
this.downloadPhaseCompleted ?
|
|
1340
|
+
this.downloadPhaseCompleted ? ERROR_DELTA_FAILED : ERROR_DOWNLOAD_FAILED,
|
|
1341
1341
|
);
|
|
1342
1342
|
logger.error(TAG, `Task execution failed: ${error.message}`);
|
|
1343
1343
|
if (params.type !== DownloadTaskParams.TASK_TYPE_CLEANUP) {
|
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
//
|
|
6
6
|
// RNOH 的异步桥以纯字符串 reject,Error 上的 `code` 属性到 JS 侧很可能已丢。
|
|
7
7
|
// 因此每个错误的消息都以 `[CODE] ` 前缀开头,JS 鸿蒙分支据此解析回 code。
|
|
8
|
-
//
|
|
8
|
+
// 本文件只依赖同样无依赖的 Texts,任何文件都可安全引用它而不会形成 import 环。
|
|
9
|
+
|
|
10
|
+
import { revealText } from './Texts';
|
|
9
11
|
|
|
10
12
|
export const ERROR_INVALID_OPTIONS = 'INVALID_OPTIONS';
|
|
11
13
|
export const ERROR_DOWNLOAD_FAILED = 'DOWNLOAD_FAILED';
|
|
12
|
-
export const
|
|
14
|
+
export const ERROR_DELTA_FAILED = revealText('0a36c0f286b44e640b1339dd');
|
|
13
15
|
export const ERROR_FILE_OPERATION_FAILED = 'FILE_OPERATION_FAILED';
|
|
14
16
|
export const ERROR_SWITCH_VERSION_FAILED = 'SWITCH_VERSION_FAILED';
|
|
15
17
|
export const ERROR_MARK_SUCCESS_FAILED = 'MARK_SUCCESS_FAILED';
|
|
@@ -23,14 +25,14 @@ export const ERROR_APK_INSTALL_FAILED = 'APK_INSTALL_FAILED';
|
|
|
23
25
|
|
|
24
26
|
/**
|
|
25
27
|
* 带稳定错误码的 Error:`code` 是属性,消息同时带 `[CODE] ` 前缀(桥丢属性时
|
|
26
|
-
* JS 仍能解析)。始终经
|
|
28
|
+
* JS 仍能解析)。始终经 createPushyError / toPushyError 构造。
|
|
27
29
|
*/
|
|
28
|
-
export class
|
|
30
|
+
export class PushyError extends Error {
|
|
29
31
|
readonly code: string;
|
|
30
32
|
|
|
31
33
|
constructor(code: string, message: string) {
|
|
32
34
|
super(`[${code}] ${message}`);
|
|
33
|
-
this.name = '
|
|
35
|
+
this.name = 'PushyError';
|
|
34
36
|
this.code = code;
|
|
35
37
|
}
|
|
36
38
|
}
|
|
@@ -46,19 +48,19 @@ export function getErrorMessage(error: unknown): string {
|
|
|
46
48
|
return String(error);
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
export function
|
|
50
|
-
return new
|
|
51
|
+
export function createPushyError(code: string, message: string): PushyError {
|
|
52
|
+
return new PushyError(code, message);
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
/**
|
|
54
|
-
* 给未带码的错误补上 `code`:已是
|
|
56
|
+
* 给未带码的错误补上 `code`:已是 PushyError 的原样返回(保留其更精确的码
|
|
55
57
|
* 与前缀,绝不叠两层前缀),其余按 defaultCode 重包并保留原消息。
|
|
56
58
|
*/
|
|
57
|
-
export function
|
|
58
|
-
if (error instanceof
|
|
59
|
+
export function toPushyError(error: unknown, defaultCode: string): PushyError {
|
|
60
|
+
if (error instanceof PushyError) {
|
|
59
61
|
return error;
|
|
60
62
|
}
|
|
61
|
-
return new
|
|
63
|
+
return new PushyError(defaultCode, getErrorMessage(error));
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
/** 从 `[CODE] message` 形式的消息里解析错误码;无前缀返回空串。 */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fileIo from '@ohos.file.fs';
|
|
2
2
|
import { util } from '@kit.ArkTS';
|
|
3
3
|
import NativePatchCore from './NativePatchCore';
|
|
4
|
-
import { ERROR_SWITCH_VERSION_FAILED,
|
|
4
|
+
import { ERROR_SWITCH_VERSION_FAILED, createPushyError } from './ErrorCodes';
|
|
5
5
|
|
|
6
6
|
// ArkTS mirror of cpp/patch_core/install_record.h — keep in sync by hand
|
|
7
7
|
// (harmony/pushy/src/test/check-constant-parity.js asserts the values match).
|
|
@@ -141,7 +141,7 @@ export function expectedBundleSha256ForActivation(
|
|
|
141
141
|
versionHash: string,
|
|
142
142
|
): string {
|
|
143
143
|
if (record === null) {
|
|
144
|
-
throw
|
|
144
|
+
throw createPushyError(
|
|
145
145
|
ERROR_SWITCH_VERSION_FAILED,
|
|
146
146
|
`Bundle version ${versionHash} has no valid completion record.`,
|
|
147
147
|
);
|
|
@@ -153,7 +153,7 @@ export function expectedBundleSha256ForActivation(
|
|
|
153
153
|
record.schema !== INSTALL_RECORD_SCHEMA ||
|
|
154
154
|
record.versionHash !== versionHash
|
|
155
155
|
) {
|
|
156
|
-
throw
|
|
156
|
+
throw createPushyError(
|
|
157
157
|
ERROR_SWITCH_VERSION_FAILED,
|
|
158
158
|
`Bundle version ${versionHash} completion record mismatch.`,
|
|
159
159
|
);
|
|
@@ -179,7 +179,7 @@ export async function verifyInstallForActivation(
|
|
|
179
179
|
}
|
|
180
180
|
const actual = await NativePatchCore.sha256HexFileAsync(bundlePath);
|
|
181
181
|
if (actual.toLowerCase() !== expected.toLowerCase()) {
|
|
182
|
-
throw
|
|
182
|
+
throw createPushyError(
|
|
183
183
|
ERROR_SWITCH_VERSION_FAILED,
|
|
184
184
|
`Bundle version ${versionHash} bundle digest mismatch.`,
|
|
185
185
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import NativeCore from 'librnpushy.so';
|
|
2
2
|
|
|
3
3
|
export const STATE_OP_SWITCH_VERSION = 1;
|
|
4
4
|
export const STATE_OP_MARK_SUCCESS = 2;
|
|
@@ -43,7 +43,7 @@ export interface FileSourcePatchRequest {
|
|
|
43
43
|
sourceRoot: string;
|
|
44
44
|
targetRoot: string;
|
|
45
45
|
originBundlePath: string;
|
|
46
|
-
|
|
46
|
+
bundleDeltaPath: string;
|
|
47
47
|
bundleOutputPath: string;
|
|
48
48
|
mergeSourceSubdir?: string;
|
|
49
49
|
enableMerge?: boolean;
|
|
@@ -64,7 +64,7 @@ interface NativePatchCoreBindings {
|
|
|
64
64
|
flagA?: boolean,
|
|
65
65
|
flagB?: boolean,
|
|
66
66
|
): StateCoreResult;
|
|
67
|
-
|
|
67
|
+
buildArchivePlan(
|
|
68
68
|
patchType: number,
|
|
69
69
|
entryNames: string[],
|
|
70
70
|
copyFroms: string[],
|
|
@@ -73,7 +73,7 @@ interface NativePatchCoreBindings {
|
|
|
73
73
|
bundlePatchEntryName?: string,
|
|
74
74
|
): ArchivePatchPlanResult;
|
|
75
75
|
buildCopyGroups(copyFroms: string[], copyTos: string[]): CopyGroupResult[];
|
|
76
|
-
|
|
76
|
+
applyDeltaFromSource(options: FileSourcePatchRequest): Promise<void>;
|
|
77
77
|
/**
|
|
78
78
|
* 删除 rootDir 下超过 maxAgeDays 且名字不在 keepNames 里的条目(native
|
|
79
79
|
* 工作线程)。keepNames 必须包含所有可能仍在使用的版本:持久化的
|
|
@@ -99,19 +99,19 @@ interface NativePatchCoreBindings {
|
|
|
99
99
|
// 更新流程决策层(cpp/update_flow_core,NATIVE_CHECKUPDATE_DESIGN §10):
|
|
100
100
|
// JSON 字符串进出,与决策层自身的边界一致。返回 undefined = 输入未通过
|
|
101
101
|
// 解析,编排器跳过本轮检测。
|
|
102
|
-
|
|
102
|
+
buildRequestBody(inputJson: string): string | undefined;
|
|
103
103
|
orderEndpointCandidates(
|
|
104
104
|
endpointsJson: string,
|
|
105
105
|
randomSample: number,
|
|
106
106
|
): string | undefined;
|
|
107
|
-
|
|
107
|
+
handleResponse(
|
|
108
108
|
responseText: string,
|
|
109
109
|
identityJson: string,
|
|
110
110
|
afterDownload: string,
|
|
111
111
|
): string | undefined;
|
|
112
|
-
// 响应 schema 门槛(update_flow_core::
|
|
112
|
+
// 响应 schema 门槛(update_flow_core::IsValidResponse):200 但只带
|
|
113
113
|
// `{"error":...}` 的节点算失败,不得阻止继续 fallback。
|
|
114
|
-
|
|
114
|
+
isValidResponse(responseText: string): boolean;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
export default
|
|
117
|
+
export default NativeCore as unknown as NativePatchCoreBindings;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ERROR_INVALID_OPTIONS,
|
|
1
|
+
import { ERROR_INVALID_OPTIONS, createPushyError } from './ErrorCodes';
|
|
2
2
|
|
|
3
3
|
// Server-controlled identifiers are used as children of the update root. Keep
|
|
4
4
|
// validation in a dependency-light module (ErrorCodes.ts has no imports) so
|
|
@@ -18,7 +18,7 @@ export function isSafePathComponent(name: string): boolean {
|
|
|
18
18
|
|
|
19
19
|
export function assertSafePathComponent(name: string): string {
|
|
20
20
|
if (!isSafePathComponent(name)) {
|
|
21
|
-
throw
|
|
21
|
+
throw createPushyError(
|
|
22
22
|
ERROR_INVALID_OPTIONS,
|
|
23
23
|
`Invalid path component: ${name}`,
|
|
24
24
|
);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { POLICY_NEXT_LAUNCH, revealText } from './Texts';
|
|
2
|
+
|
|
1
3
|
/** Options accepted by PushyFileJSBundleProvider.configure(). */
|
|
2
4
|
export interface PushyConfiguration {
|
|
3
5
|
appKey: string;
|
|
@@ -26,12 +28,12 @@ interface PersistedPushyConfiguration {
|
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
const DEFAULT_ENDPOINTS: string[] = [
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
+
revealText('3203e0c1bdd1270a372f18f8c2b6de7f4f2607f5b3d5b9817b592947e5cdefbc8a7e'),
|
|
32
|
+
revealText('3203e0c1bdd1270a372f18f8c2b6de7f4f2607f5f0daac9c644a620ae88ca1ad93'),
|
|
31
33
|
];
|
|
32
34
|
const DEFAULT_QUERY_URLS: string[] = [
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
+
revealText('3203e0c1bdd1270a253608fcd3fd9362476817f4f0d5a1996342631be3c2a3a9d779552507fdcde8928a6f512f5ce2ccbdc869404d2f1de79daa826d562c0913eec4fa9b7d4426'),
|
|
36
|
+
revealText('3203e0c1bdd1270a213b12b7dca09468462e12f3b0d5bd813d482446f4c6a1be8e79552507fdcda68cd06e5c3710e480a4867048483e55e0c2ab8d7d43030d1ce9c3b183214e2601f2f0d5b782601e2719e8ca'),
|
|
35
37
|
];
|
|
36
38
|
const CONFIG_KEYS: string[] = [
|
|
37
39
|
'appKey', 'endpoints', 'queryUrls', 'afterDownload', 'disabled',
|
|
@@ -84,8 +86,8 @@ export function normalizePushyConfiguration(options: PushyConfiguration): string
|
|
|
84
86
|
'queryUrls', false,
|
|
85
87
|
);
|
|
86
88
|
const afterDownload = options.afterDownload === undefined ? 'none' : options.afterDownload;
|
|
87
|
-
if (afterDownload !== 'none' && afterDownload !==
|
|
88
|
-
throw new Error(
|
|
89
|
+
if (afterDownload !== 'none' && afterDownload !== POLICY_NEXT_LAUNCH) {
|
|
90
|
+
throw new Error(`Invalid native configuration: afterDownload must be none or ${POLICY_NEXT_LAUNCH}`);
|
|
89
91
|
}
|
|
90
92
|
if (options.disabled !== undefined && typeof options.disabled !== 'boolean') {
|
|
91
93
|
throw new Error('Invalid native configuration: disabled must be a boolean');
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
import common from '@ohos.app.ability.common';
|
|
7
7
|
import fs from '@ohos.file.fs';
|
|
8
8
|
import { UpdateContext } from './UpdateContext';
|
|
9
|
-
import { prepareBundleNative } from './
|
|
9
|
+
import { prepareBundleNative } from './SyncCoordinator';
|
|
10
10
|
import type { BundlePreparationResult } from './BundlePreparationResult';
|
|
11
11
|
import { normalizePushyConfiguration } from './PushyConfiguration';
|
|
12
12
|
import type { PushyConfiguration } from './PushyConfiguration';
|