react-native-update 10.55.0 → 10.56.0
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/README-CN.md +35 -0
- package/README.md +37 -0
- package/android/build.gradle +53 -27
- package/android/jni/Application.mk +6 -1
- package/android/jni/hpatch.c +59 -4
- 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
- package/android/proguard.pro +70 -14
- package/android/src/main/java/cn/reactnative/modules/update/ApkInstaller.java +15 -8
- package/android/src/main/java/cn/reactnative/modules/update/ArchiveLimits.java +1 -0
- package/android/src/main/java/cn/reactnative/modules/update/BundledResourceCopier.java +1 -2
- package/android/src/main/java/cn/reactnative/modules/update/CleanupKeepNames.java +25 -0
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +98 -34
- package/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java +29 -0
- package/android/src/main/java/cn/reactnative/modules/update/FileOperationException.java +15 -0
- package/android/src/main/java/cn/reactnative/modules/update/HttpUtils.java +59 -0
- package/android/src/main/java/cn/reactnative/modules/update/InstallRecord.java +27 -1
- package/android/src/main/java/cn/reactnative/modules/update/NativeCheckOrchestrator.java +57 -16
- package/android/src/main/java/cn/reactnative/modules/update/ReactReloadManager.java +38 -46
- package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +279 -183
- package/android/src/main/java/cn/reactnative/modules/update/UpdateFileUtils.java +14 -0
- package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleImpl.java +222 -215
- package/android/src/main/java/cn/reactnative/modules/update/UpdatePackage.java +5 -0
- package/android/src/newarch/cn/reactnative/modules/update/UpdateModule.java +23 -25
- package/android/src/oldarch/cn/reactnative/modules/update/UpdateModule.java +29 -29
- package/cpp/patch_core/archive_limits.h +6 -0
- package/cpp/patch_core/jni_util.h +6 -2
- package/cpp/patch_core/patch_core.cpp +237 -84
- package/cpp/patch_core/patch_core.h +11 -0
- package/cpp/patch_core/patch_core_android.cpp +49 -33
- package/cpp/patch_core/update_core_android.cpp +134 -103
- package/cpp/update_flow_core/flow_json.cpp +150 -17
- package/cpp/update_flow_core/flow_json.h +13 -1
- package/cpp/update_flow_core/update_flow_core.cpp +78 -8
- package/cpp/update_flow_core/update_flow_core.h +9 -0
- package/cpp/update_flow_core/update_flow_jni.cpp +44 -25
- package/harmony/hvigor-plugin.ts +168 -21
- package/harmony/pushy/src/README.md +74 -0
- package/harmony/pushy/src/main/cpp/CMakeLists.txt +13 -3
- package/harmony/pushy/src/main/cpp/PushyTurboModule.cpp +11 -5
- package/harmony/pushy/src/main/cpp/pushy.cpp +103 -18
- package/harmony/pushy/src/main/ets/ArchiveLimits.ts +61 -5
- package/harmony/pushy/src/main/ets/DownloadTask.ts +255 -166
- package/harmony/pushy/src/main/ets/ErrorCodes.ts +75 -0
- package/harmony/pushy/src/main/ets/EventHub.ts +22 -20
- package/harmony/pushy/src/main/ets/InstallRecord.ts +79 -21
- package/harmony/pushy/src/main/ets/Logger.ts +42 -16
- package/harmony/pushy/src/main/ets/NativeCheckOrchestrator.ts +98 -17
- package/harmony/pushy/src/main/ets/NativePatchCore.ts +10 -3
- package/harmony/pushy/src/main/ets/PathUtils.ts +9 -3
- package/harmony/pushy/src/main/ets/PushyTurboModule.ts +139 -53
- package/harmony/pushy/src/main/ets/UpdateContext.ts +269 -159
- package/harmony/pushy/src/main/module.json5 +5 -1
- package/harmony/pushy.har +0 -0
- package/ios/PrivacyInfo.xcprivacy +79 -0
- package/ios/RCTPushy/RCTPushy.mm +406 -170
- package/ios/RCTPushy/RCTPushyDownloader.h +14 -0
- package/ios/RCTPushy/RCTPushyDownloader.mm +136 -11
- package/package.json +14 -24
- package/react-native-update.podspec +36 -3
- package/src/client.ts +244 -86
- package/src/context.ts +40 -27
- package/src/core.ts +41 -16
- package/src/endpoint.ts +2 -1
- package/src/error.ts +49 -11
- package/src/index.ts +14 -0
- package/src/locales/en.ts +1 -23
- package/src/locales/zh.ts +1 -22
- package/src/provider.tsx +159 -111
- package/src/type.ts +0 -2
- package/src/updateFlowCore.ts +10 -2
- package/src/utils.ts +61 -4
- package/android/src/main/java/cn/reactnative/modules/update/ReactNativeHostHandler.java +0 -13
- package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleSupport.java +0 -45
package/src/core.ts
CHANGED
|
@@ -12,12 +12,13 @@ const isTurboModuleEnabled =
|
|
|
12
12
|
// https://github.com/facebook/react-native/pull/48362
|
|
13
13
|
(global as any).__turboModuleProxy || (global as any).RN$Bridgeless;
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
const isWebPlatform = Platform.OS === 'web';
|
|
16
|
+
|
|
17
|
+
export const PushyModule = isWebPlatform
|
|
18
|
+
? emptyModule
|
|
19
|
+
: isTurboModuleEnabled
|
|
20
|
+
? require('./NativePushy').default
|
|
21
|
+
: NativeModules.Pushy;
|
|
21
22
|
|
|
22
23
|
export const UpdateModule = PushyModule;
|
|
23
24
|
|
|
@@ -28,9 +29,27 @@ if (!PushyModule) {
|
|
|
28
29
|
);
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
// On web PushyModule is a Proxy whose every property is a noop function, so
|
|
33
|
+
// reading the constants off it would make each of them a function:
|
|
34
|
+
// `isRolledBack` true (a fake rollback report on every page load), a uuid of
|
|
35
|
+
// "() => {}", JSON.parse failing on currentVersionInfo. Web has no native
|
|
36
|
+
// state at all, so its constants are the explicit empty ones.
|
|
37
|
+
const PushyConstants: Record<string, any> = isWebPlatform
|
|
38
|
+
? {
|
|
39
|
+
downloadRootDir: '',
|
|
40
|
+
packageVersion: '',
|
|
41
|
+
currentVersion: '',
|
|
42
|
+
isFirstTime: false,
|
|
43
|
+
rolledBackVersion: '',
|
|
44
|
+
buildTime: '',
|
|
45
|
+
uuid: '',
|
|
46
|
+
currentVersionInfo: '',
|
|
47
|
+
supportedDiffVersion: 0,
|
|
48
|
+
currentBundleSha256: '',
|
|
49
|
+
}
|
|
50
|
+
: isTurboModuleEnabled
|
|
51
|
+
? PushyModule.getConstants()
|
|
52
|
+
: PushyModule;
|
|
34
53
|
|
|
35
54
|
export const downloadRootDir: string = PushyConstants.downloadRootDir;
|
|
36
55
|
export const packageVersion: string = PushyConstants.packageVersion;
|
|
@@ -97,9 +116,9 @@ let uuid = PushyConstants.uuid;
|
|
|
97
116
|
// a check for.
|
|
98
117
|
let bundleHash = '';
|
|
99
118
|
// The JS layer can arrive via hot update onto an older binary whose native
|
|
100
|
-
// module predates the method, hence the feature detect
|
|
101
|
-
//
|
|
102
|
-
if (typeof PushyModule.getBundleHash === 'function') {
|
|
119
|
+
// module predates the method, hence the feature detect (on web the noop Proxy
|
|
120
|
+
// would pass it, so web is excluded explicitly).
|
|
121
|
+
if (!isWebPlatform && typeof PushyModule.getBundleHash === 'function') {
|
|
103
122
|
Promise.resolve(PushyModule.getBundleHash())
|
|
104
123
|
.then((hash: unknown) => {
|
|
105
124
|
bundleHash = String(hash || '');
|
|
@@ -112,8 +131,11 @@ if (typeof PushyModule.getBundleHash === 'function') {
|
|
|
112
131
|
/** '' while unknown (not yet computed, debug build, no embedded bundle, older native). */
|
|
113
132
|
export const getBundleHash = (): string => bundleHash;
|
|
114
133
|
|
|
134
|
+
// Natives resolve null/'' when no record exists for the hash (only a record
|
|
135
|
+
// that exists and is not a JSON object rejects with INVALID_HASH_INFO).
|
|
115
136
|
async function getLocalHashInfo(hash: string) {
|
|
116
|
-
|
|
137
|
+
const raw = await PushyModule.getLocalHashInfo(hash);
|
|
138
|
+
return raw ? JSON.parse(raw) : null;
|
|
117
139
|
}
|
|
118
140
|
|
|
119
141
|
// @deprecated use currentVersionInfo instead
|
|
@@ -131,9 +153,12 @@ if (!uuid) {
|
|
|
131
153
|
uuid = require('nanoid/non-secure').nanoid();
|
|
132
154
|
// If persisting fails the uuid drifts on every launch, which skews gray
|
|
133
155
|
// release bucketing and inflates stats — log it instead of failing silently.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
156
|
+
// Web has nowhere to persist it (nothing runs there anyway).
|
|
157
|
+
if (!isWebPlatform) {
|
|
158
|
+
Promise.resolve(PushyModule.setUuid(uuid)).catch((e: any) => {
|
|
159
|
+
log('setUuid error:', e?.message || e);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
137
162
|
}
|
|
138
163
|
|
|
139
164
|
export const cInfo = {
|
package/src/endpoint.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { UpdateError } from './error';
|
|
2
|
+
import i18n from './i18n';
|
|
2
3
|
import { dedupeEndpoints, orderEndpointCandidates } from './updateFlowCore';
|
|
3
4
|
|
|
4
5
|
export { dedupeEndpoints };
|
|
@@ -146,7 +147,7 @@ export async function executeEndpointFallback<T>({
|
|
|
146
147
|
let candidates = orderEndpointCandidates(configuredEndpoints, random());
|
|
147
148
|
|
|
148
149
|
if (!candidates.length) {
|
|
149
|
-
throw new UpdateError('
|
|
150
|
+
throw new UpdateError(i18n.t('error_no_endpoints'), 'NO_ENDPOINTS');
|
|
150
151
|
}
|
|
151
152
|
|
|
152
153
|
const firstEndpoint = candidates[0];
|
package/src/error.ts
CHANGED
|
@@ -20,8 +20,6 @@ export type UpdateErrorCode =
|
|
|
20
20
|
| 'SWITCH_VERSION_FAILED'
|
|
21
21
|
| 'MARK_SUCCESS_FAILED'
|
|
22
22
|
| 'APK_INSTALL_PENDING'
|
|
23
|
-
| 'STORAGE_PERMISSION_REJECTED'
|
|
24
|
-
| 'STORAGE_PERMISSION_ERROR'
|
|
25
23
|
| 'APK_DOWNLOAD_FAILED'
|
|
26
24
|
// A throw from a user-provided hook (e.g. beforeReload) — not an update
|
|
27
25
|
// pipeline failure, and excluded from server-side patch-health telemetry.
|
|
@@ -51,8 +49,6 @@ const KNOWN_CODES = new Set<string>([
|
|
|
51
49
|
'SWITCH_VERSION_FAILED',
|
|
52
50
|
'MARK_SUCCESS_FAILED',
|
|
53
51
|
'APK_INSTALL_PENDING',
|
|
54
|
-
'STORAGE_PERMISSION_REJECTED',
|
|
55
|
-
'STORAGE_PERMISSION_ERROR',
|
|
56
52
|
'APK_DOWNLOAD_FAILED',
|
|
57
53
|
'USER_HOOK_ERROR',
|
|
58
54
|
'SINGLETON_VIOLATION',
|
|
@@ -79,6 +75,37 @@ export const asUpdateErrorCode = (
|
|
|
79
75
|
? (code as UpdateErrorCode)
|
|
80
76
|
: undefined;
|
|
81
77
|
|
|
78
|
+
// A bridge that cannot put the code on the rejection's `code` property
|
|
79
|
+
// (Harmony's TurboModule rejections carry only a message) prefixes the
|
|
80
|
+
// message with it instead: `[PATCH_FAILED] copiesCrc mismatch`.
|
|
81
|
+
const CODED_MESSAGE = /^\[([A-Z_]+)\] /;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The stable code a thrown value carries, if any, and its message with the
|
|
85
|
+
* `[CODE] ` prefix (see CODED_MESSAGE) stripped. A `code` property wins over
|
|
86
|
+
* the prefix; an unknown code in either place counts as absent.
|
|
87
|
+
*/
|
|
88
|
+
export const readErrorCode = (
|
|
89
|
+
e: unknown
|
|
90
|
+
): { code?: UpdateErrorCode; message: string } => {
|
|
91
|
+
const source =
|
|
92
|
+
e !== null && typeof e === 'object'
|
|
93
|
+
? (e as { code?: unknown; message?: unknown })
|
|
94
|
+
: undefined;
|
|
95
|
+
const raw =
|
|
96
|
+
typeof e === 'string'
|
|
97
|
+
? e
|
|
98
|
+
: typeof source?.message === 'string'
|
|
99
|
+
? source.message
|
|
100
|
+
: '';
|
|
101
|
+
const match = CODED_MESSAGE.exec(raw);
|
|
102
|
+
const prefixed = match ? asUpdateErrorCode(match[1]) : undefined;
|
|
103
|
+
return {
|
|
104
|
+
code: asUpdateErrorCode(source?.code) ?? prefixed,
|
|
105
|
+
message: prefixed && match ? raw.slice(match[0].length) : raw,
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
|
|
82
109
|
export class UpdateError extends Error {
|
|
83
110
|
code: UpdateErrorCode;
|
|
84
111
|
cause?: unknown;
|
|
@@ -101,9 +128,11 @@ export class UpdateError extends Error {
|
|
|
101
128
|
* Attach a code to an unknown thrown value. An existing Error keeps its
|
|
102
129
|
* identity (message, stack, and any known code already assigned upstream) so
|
|
103
130
|
* callers comparing the caught error to the original still match; non-Error
|
|
104
|
-
* values are wrapped. A
|
|
105
|
-
*
|
|
106
|
-
*
|
|
131
|
+
* values are wrapped. A code carried as a `[CODE] ` message prefix is moved
|
|
132
|
+
* onto the `code` property (and off the message) exactly as if the bridge
|
|
133
|
+
* had set it. A foreign `code` (axios/system errors) is overwritten with
|
|
134
|
+
* ours; a frozen/sealed Error that rejects the assignment is wrapped instead
|
|
135
|
+
* (identity is lost only in that edge case).
|
|
107
136
|
*/
|
|
108
137
|
export const toUpdateError = (
|
|
109
138
|
e: unknown,
|
|
@@ -112,14 +141,23 @@ export const toUpdateError = (
|
|
|
112
141
|
if (e instanceof Error) {
|
|
113
142
|
const err = e as UpdateError;
|
|
114
143
|
if (!asUpdateErrorCode(err.code)) {
|
|
144
|
+
const parsed = readErrorCode(err);
|
|
145
|
+
const resolved = parsed.code ?? code;
|
|
115
146
|
try {
|
|
116
|
-
err.code =
|
|
147
|
+
err.code = resolved;
|
|
148
|
+
if (parsed.code) {
|
|
149
|
+
err.message = parsed.message;
|
|
150
|
+
}
|
|
117
151
|
} catch {}
|
|
118
|
-
if (err.code !==
|
|
119
|
-
return new UpdateError(
|
|
152
|
+
if (err.code !== resolved) {
|
|
153
|
+
return new UpdateError(parsed.message, resolved, { cause: err });
|
|
120
154
|
}
|
|
121
155
|
}
|
|
122
156
|
return err;
|
|
123
157
|
}
|
|
124
|
-
|
|
158
|
+
const parsed = readErrorCode(e);
|
|
159
|
+
return new UpdateError(
|
|
160
|
+
parsed.code ? parsed.message : String(e ?? code),
|
|
161
|
+
parsed.code ?? code
|
|
162
|
+
);
|
|
125
163
|
};
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type { UpdateErrorListener } from './client';
|
|
2
2
|
export { Cresc, Pushy } from './client';
|
|
3
|
+
export type { UpdateContextValue } from './context';
|
|
3
4
|
export {
|
|
4
5
|
ProgressContext,
|
|
5
6
|
UpdateContext,
|
|
@@ -25,3 +26,16 @@ export {
|
|
|
25
26
|
updateMetadataTags,
|
|
26
27
|
} from './metadata';
|
|
27
28
|
export { PushyProvider, UpdateProvider } from './provider';
|
|
29
|
+
export type {
|
|
30
|
+
BeforeReloadContext,
|
|
31
|
+
CheckResult,
|
|
32
|
+
ClientOptions,
|
|
33
|
+
EventData,
|
|
34
|
+
EventType,
|
|
35
|
+
ProgressData,
|
|
36
|
+
UpdateCheckState,
|
|
37
|
+
UpdateEventsLogger,
|
|
38
|
+
UpdateServerConfig,
|
|
39
|
+
UpdateTestPayload,
|
|
40
|
+
VersionInfo,
|
|
41
|
+
} from './type';
|
package/src/locales/en.ts
CHANGED
|
@@ -1,31 +1,10 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
// Common messages
|
|
3
3
|
checking_update: 'Checking for updates...',
|
|
4
|
-
downloading_update: 'Downloading update package...',
|
|
5
|
-
installing_update: 'Installing update...',
|
|
6
|
-
update_available: 'Update available',
|
|
7
|
-
update_downloaded: 'Update downloaded successfully',
|
|
8
|
-
update_installed: 'Update installed successfully',
|
|
9
|
-
no_update_available: 'You are up to date',
|
|
10
4
|
update_failed: 'Update failed',
|
|
11
|
-
network_error: 'Network connection error',
|
|
12
|
-
download_failed: 'Download failed',
|
|
13
|
-
install_failed: 'Installation failed',
|
|
14
5
|
|
|
15
6
|
// Progress messages with interpolation
|
|
16
7
|
download_progress: 'Download progress: {{progress}}%',
|
|
17
|
-
download_speed: 'Download speed: {{speed}}/s',
|
|
18
|
-
file_size: 'File size: {{size}}',
|
|
19
|
-
time_remaining: 'Time remaining: {{time}}',
|
|
20
|
-
|
|
21
|
-
// Error messages
|
|
22
|
-
error_message: 'Error message: {{message}}',
|
|
23
|
-
retry_count: 'Retry attempt: {{count}}/{{max}}',
|
|
24
|
-
|
|
25
|
-
// Update info
|
|
26
|
-
version_info: 'Version {{version}} ({{build}})',
|
|
27
|
-
release_notes: 'Release notes: {{notes}}',
|
|
28
|
-
update_size: 'Update size: {{size}}MB',
|
|
29
8
|
|
|
30
9
|
// Alert messages
|
|
31
10
|
alert_title: 'Notice',
|
|
@@ -43,6 +22,7 @@ export default {
|
|
|
43
22
|
|
|
44
23
|
// Error messages
|
|
45
24
|
error_appkey_required: 'appKey is required',
|
|
25
|
+
error_no_endpoints: 'No update server endpoints configured',
|
|
46
26
|
error_client_singleton:
|
|
47
27
|
'A second update client was created: the SDK is a process-level singleton — create one client and reuse it everywhere (use client.setOptions to change options at runtime)',
|
|
48
28
|
error_provider_singleton:
|
|
@@ -67,8 +47,6 @@ export default {
|
|
|
67
47
|
error_http_status: '{{status}} {{statusText}}',
|
|
68
48
|
error_apk_pending_install:
|
|
69
49
|
'The APK has been downloaded, please complete the installation in the system installer',
|
|
70
|
-
error_storage_permission_rejected:
|
|
71
|
-
'Storage permission denied, unable to download the APK',
|
|
72
50
|
error_apk_download_failed: 'Failed to download or install the APK',
|
|
73
51
|
error_reset_not_supported:
|
|
74
52
|
'resetToPackagedBundle is not available in the installed native module. Please rebuild the app with the latest react-native-update.',
|
package/src/locales/zh.ts
CHANGED
|
@@ -1,31 +1,10 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
// Common messages
|
|
3
3
|
checking_update: '正在检查更新...',
|
|
4
|
-
downloading_update: '正在下载更新包...',
|
|
5
|
-
installing_update: '正在安装更新...',
|
|
6
|
-
update_available: '发现新版本',
|
|
7
|
-
update_downloaded: '更新包下载完成',
|
|
8
|
-
update_installed: '更新安装完成',
|
|
9
|
-
no_update_available: '已是最新版本',
|
|
10
4
|
update_failed: '更新失败',
|
|
11
|
-
network_error: '网络连接错误',
|
|
12
|
-
download_failed: '下载失败',
|
|
13
|
-
install_failed: '安装失败',
|
|
14
5
|
|
|
15
6
|
// Progress messages with interpolation
|
|
16
7
|
download_progress: '下载进度: {{progress}}%',
|
|
17
|
-
download_speed: '下载速度: {{speed}}/s',
|
|
18
|
-
file_size: '文件大小: {{size}}',
|
|
19
|
-
time_remaining: '剩余时间: {{time}}',
|
|
20
|
-
|
|
21
|
-
// Error messages
|
|
22
|
-
error_message: '错误信息: {{message}}',
|
|
23
|
-
retry_count: '重试次数: {{count}}/{{max}}',
|
|
24
|
-
|
|
25
|
-
// Update info
|
|
26
|
-
version_info: '版本 {{version}} ({{build}})',
|
|
27
|
-
release_notes: '更新说明: {{notes}}',
|
|
28
|
-
update_size: '更新包大小: {{size}}MB',
|
|
29
8
|
|
|
30
9
|
// Alert messages
|
|
31
10
|
alert_title: '提示',
|
|
@@ -41,6 +20,7 @@ export default {
|
|
|
41
20
|
|
|
42
21
|
// Error messages
|
|
43
22
|
error_appkey_required: '需要提供 appKey',
|
|
23
|
+
error_no_endpoints: '未配置更新服务器端点',
|
|
44
24
|
error_client_singleton:
|
|
45
25
|
'检测到重复创建热更新 client:SDK 是进程级单例,请只创建一个 client 并在全局复用(运行期改配置请调用 client.setOptions)',
|
|
46
26
|
error_provider_singleton:
|
|
@@ -62,7 +42,6 @@ export default {
|
|
|
62
42
|
error_invalid_check_response: '更新检查响应不是有效结果',
|
|
63
43
|
error_http_status: '{{status}} {{statusText}}',
|
|
64
44
|
error_apk_pending_install: '安装包已下载完成,请在系统安装界面完成安装',
|
|
65
|
-
error_storage_permission_rejected: '存储权限被拒绝,无法下载安装包',
|
|
66
45
|
error_apk_download_failed: '安装包下载或安装失败',
|
|
67
46
|
error_reset_not_supported:
|
|
68
47
|
'当前安装的原生模块不支持 resetToPackagedBundle,请使用最新版 react-native-update 重新打包后再试。',
|