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.
Files changed (76) hide show
  1. package/README-CN.md +35 -0
  2. package/README.md +37 -0
  3. package/android/build.gradle +53 -27
  4. package/android/jni/Application.mk +6 -1
  5. package/android/jni/hpatch.c +59 -4
  6. package/android/lib/arm64-v8a/librnupdate.so +0 -0
  7. package/android/lib/armeabi-v7a/librnupdate.so +0 -0
  8. package/android/lib/x86/librnupdate.so +0 -0
  9. package/android/lib/x86_64/librnupdate.so +0 -0
  10. package/android/proguard.pro +70 -14
  11. package/android/src/main/java/cn/reactnative/modules/update/ApkInstaller.java +15 -8
  12. package/android/src/main/java/cn/reactnative/modules/update/ArchiveLimits.java +1 -0
  13. package/android/src/main/java/cn/reactnative/modules/update/BundledResourceCopier.java +1 -2
  14. package/android/src/main/java/cn/reactnative/modules/update/CleanupKeepNames.java +25 -0
  15. package/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +98 -34
  16. package/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java +29 -0
  17. package/android/src/main/java/cn/reactnative/modules/update/FileOperationException.java +15 -0
  18. package/android/src/main/java/cn/reactnative/modules/update/HttpUtils.java +59 -0
  19. package/android/src/main/java/cn/reactnative/modules/update/InstallRecord.java +27 -1
  20. package/android/src/main/java/cn/reactnative/modules/update/NativeCheckOrchestrator.java +57 -16
  21. package/android/src/main/java/cn/reactnative/modules/update/ReactReloadManager.java +38 -46
  22. package/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +279 -183
  23. package/android/src/main/java/cn/reactnative/modules/update/UpdateFileUtils.java +14 -0
  24. package/android/src/main/java/cn/reactnative/modules/update/UpdateModuleImpl.java +222 -215
  25. package/android/src/main/java/cn/reactnative/modules/update/UpdatePackage.java +5 -0
  26. package/android/src/newarch/cn/reactnative/modules/update/UpdateModule.java +23 -25
  27. package/android/src/oldarch/cn/reactnative/modules/update/UpdateModule.java +29 -29
  28. package/cpp/patch_core/archive_limits.h +6 -0
  29. package/cpp/patch_core/jni_util.h +6 -2
  30. package/cpp/patch_core/patch_core.cpp +237 -84
  31. package/cpp/patch_core/patch_core.h +11 -0
  32. package/cpp/patch_core/patch_core_android.cpp +49 -33
  33. package/cpp/patch_core/update_core_android.cpp +134 -103
  34. package/cpp/update_flow_core/flow_json.cpp +150 -17
  35. package/cpp/update_flow_core/flow_json.h +13 -1
  36. package/cpp/update_flow_core/update_flow_core.cpp +78 -8
  37. package/cpp/update_flow_core/update_flow_core.h +9 -0
  38. package/cpp/update_flow_core/update_flow_jni.cpp +44 -25
  39. package/harmony/hvigor-plugin.ts +168 -21
  40. package/harmony/pushy/src/README.md +74 -0
  41. package/harmony/pushy/src/main/cpp/CMakeLists.txt +13 -3
  42. package/harmony/pushy/src/main/cpp/PushyTurboModule.cpp +11 -5
  43. package/harmony/pushy/src/main/cpp/pushy.cpp +103 -18
  44. package/harmony/pushy/src/main/ets/ArchiveLimits.ts +61 -5
  45. package/harmony/pushy/src/main/ets/DownloadTask.ts +255 -166
  46. package/harmony/pushy/src/main/ets/ErrorCodes.ts +75 -0
  47. package/harmony/pushy/src/main/ets/EventHub.ts +22 -20
  48. package/harmony/pushy/src/main/ets/InstallRecord.ts +79 -21
  49. package/harmony/pushy/src/main/ets/Logger.ts +42 -16
  50. package/harmony/pushy/src/main/ets/NativeCheckOrchestrator.ts +98 -17
  51. package/harmony/pushy/src/main/ets/NativePatchCore.ts +10 -3
  52. package/harmony/pushy/src/main/ets/PathUtils.ts +9 -3
  53. package/harmony/pushy/src/main/ets/PushyTurboModule.ts +139 -53
  54. package/harmony/pushy/src/main/ets/UpdateContext.ts +269 -159
  55. package/harmony/pushy/src/main/module.json5 +5 -1
  56. package/harmony/pushy.har +0 -0
  57. package/ios/PrivacyInfo.xcprivacy +79 -0
  58. package/ios/RCTPushy/RCTPushy.mm +406 -170
  59. package/ios/RCTPushy/RCTPushyDownloader.h +14 -0
  60. package/ios/RCTPushy/RCTPushyDownloader.mm +136 -11
  61. package/package.json +14 -24
  62. package/react-native-update.podspec +36 -3
  63. package/src/client.ts +244 -86
  64. package/src/context.ts +40 -27
  65. package/src/core.ts +41 -16
  66. package/src/endpoint.ts +2 -1
  67. package/src/error.ts +49 -11
  68. package/src/index.ts +14 -0
  69. package/src/locales/en.ts +1 -23
  70. package/src/locales/zh.ts +1 -22
  71. package/src/provider.tsx +159 -111
  72. package/src/type.ts +0 -2
  73. package/src/updateFlowCore.ts +10 -2
  74. package/src/utils.ts +61 -4
  75. package/android/src/main/java/cn/reactnative/modules/update/ReactNativeHostHandler.java +0 -13
  76. 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
- export const PushyModule =
16
- Platform.OS === 'web'
17
- ? emptyModule
18
- : isTurboModuleEnabled
19
- ? require('./NativePushy').default
20
- : NativeModules.Pushy;
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
- const PushyConstants = isTurboModuleEnabled
32
- ? PushyModule.getConstants()
33
- : PushyModule;
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. On web PushyModule is
101
- // a noop Proxy and the call settles to ''.
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
- return JSON.parse(await PushyModule.getLocalHashInfo(hash));
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
- Promise.resolve(PushyModule.setUuid(uuid)).catch((e: any) => {
135
- log('setUuid error:', e?.message || e);
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('No endpoints configured', 'NO_ENDPOINTS');
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 foreign `code` (axios/system errors) is overwritten
105
- * with ours; a frozen/sealed Error that rejects the assignment is wrapped
106
- * instead (identity is lost only in that edge case).
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 = code;
147
+ err.code = resolved;
148
+ if (parsed.code) {
149
+ err.message = parsed.message;
150
+ }
117
151
  } catch {}
118
- if (err.code !== code) {
119
- return new UpdateError(err.message, code, { cause: err });
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
- return new UpdateError(String(e ?? code), code);
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 重新打包后再试。',