react-native-update 10.37.13 → 10.37.16

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 (71) hide show
  1. package/README-CN.md +72 -0
  2. package/README.md +61 -49
  3. package/android/build.gradle +47 -17
  4. package/bunfig.toml +2 -0
  5. package/error.js +1609 -0
  6. package/harmony/har-wrapper/AppScope/app.json5 +8 -0
  7. package/harmony/har-wrapper/build-profile.json5 +35 -0
  8. package/harmony/har-wrapper/hvigor/hvigor-config.json5 +5 -0
  9. package/harmony/har-wrapper/hvigorfile.ts +6 -0
  10. package/harmony/har-wrapper/oh-package.json5 +4 -0
  11. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/cache-v2-77b153ce45aba0ed28ef.json +1415 -0
  12. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/cmakeFiles-v1-b65a07793384e0ce3e08.json +809 -0
  13. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/codemodel-v2-ce0e89410afd8bf3a057.json +60 -0
  14. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/directory-.-Release-f5ebdc15457944623624.json +14 -0
  15. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/index-2026-03-16T14-00-08-0134.json +89 -0
  16. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.cmake/api/v1/reply/target-rnupdate-Release-267153624504c9c3ffdd.json +222 -0
  17. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.ninja_deps +0 -0
  18. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/.ninja_log +8 -0
  19. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeCache.txt +415 -0
  20. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeCCompiler.cmake +74 -0
  21. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeCXXCompiler.cmake +85 -0
  22. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeDetermineCompilerABI_C.bin +0 -0
  23. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeDetermineCompilerABI_CXX.bin +0 -0
  24. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CMakeSystem.cmake +15 -0
  25. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CompilerIdC/CMakeCCompilerId.c +880 -0
  26. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CompilerIdC/CMakeCCompilerId.o +0 -0
  27. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CompilerIdCXX/CMakeCXXCompilerId.cpp +869 -0
  28. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/3.28.2/CompilerIdCXX/CMakeCXXCompilerId.o +0 -0
  29. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/CMakeConfigureLog.yaml +388 -0
  30. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/TargetDirectories.txt +3 -0
  31. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/cmake.check_cache +1 -0
  32. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/__w/react-native-update/react-native-update/android/jni/HDiffPatch/file_for_patch.c.o +0 -0
  33. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/__w/react-native-update/react-native-update/android/jni/HDiffPatch/libHDiffPatch/HPatch/patch.c.o +0 -0
  34. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/__w/react-native-update/react-native-update/android/jni/hpatch.c.o +0 -0
  35. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/__w/react-native-update/react-native-update/android/jni/lzma/C/Lzma2Dec.c.o +0 -0
  36. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/__w/react-native-update/react-native-update/android/jni/lzma/C/LzmaDec.c.o +0 -0
  37. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rnupdate.dir/pushy.c.o +0 -0
  38. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/CMakeFiles/rules.ninja +64 -0
  39. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/additional_project_files.txt +0 -0
  40. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/build.ninja +206 -0
  41. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/build_file_index.txt +1 -0
  42. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/cmake_install.cmake +54 -0
  43. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/compile_commands.json +38 -0
  44. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/configure_fingerprint.json +1 -0
  45. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/hvigor_native_config.json +1 -0
  46. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/metadata_generation_command.txt +17 -0
  47. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/native_work_dir.txt +1 -0
  48. package/harmony/pushy/.cxx/default/default/release/arm64-v8a/output.log +14 -0
  49. package/harmony/pushy/.cxx/default/default/release/hvigor/arm64-v8a/summary.cmake +0 -0
  50. package/harmony/pushy/BuildProfile.ets +3 -5
  51. package/harmony/pushy/build-profile.json5 +8 -1
  52. package/harmony/pushy/oh-package-lock.json5 +1 -1
  53. package/harmony/pushy/src/main/cpp/CMakeLists.txt +42 -30
  54. package/harmony/pushy/src/main/ets/DownloadTask.ts +145 -106
  55. package/harmony/pushy/src/main/ets/Logger.ts +24 -7
  56. package/harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets +1 -1
  57. package/harmony/pushy/src/main/ets/PushyTurboModule.ts +9 -73
  58. package/harmony/pushy/src/main/ets/UpdateContext.ts +206 -70
  59. package/harmony/pushy.har +0 -0
  60. package/package.json +9 -6
  61. package/scripts/build-harmony-har.js +427 -0
  62. package/scripts/prepublish.ts +113 -0
  63. package/scripts/read.js +188 -0
  64. package/src/__tests__/core.test.ts +103 -0
  65. package/src/__tests__/setup.ts +37 -0
  66. package/src/__tests__/utils.test.ts +36 -0
  67. package/src/client.ts +17 -10
  68. package/src/core.ts +5 -5
  69. package/src/locales/en.ts +1 -0
  70. package/src/locales/zh.ts +1 -0
  71. package/src/utils.ts +13 -1
@@ -0,0 +1,188 @@
1
+ const fs = require('fs');
2
+
3
+ /**
4
+ * 从 Hermes 字节码文件中读取 metadata
5
+ * 支持两种方式:
6
+ * 1. 从文件末尾的自定义 Meta 块读取(新方式,推荐)
7
+ * 2. 从 Hermes -meta 参数注入的数据读取(旧方式,备用)
8
+ */
9
+ function readBundleMetadata(filePath) {
10
+ const buffer = fs.readFileSync(filePath);
11
+
12
+ // 优先尝试从文件末尾的自定义 Meta 块读取
13
+ const metaFromFooter = readMetadataFromHBCFooter(buffer);
14
+ if (metaFromFooter) {
15
+ return metaFromFooter;
16
+ }
17
+
18
+ // 如果末尾没有 Meta 块,尝试从 Hermes -meta 参数注入的数据读取(备用方案)
19
+ console.log('No metadata footer found, trying to read from Hermes -meta parameter...');
20
+ return readMetadataFromHermesMeta(buffer);
21
+ }
22
+
23
+ /**
24
+ * 从 HBC 文件末尾的自定义 Meta 块读取元数据
25
+ * 格式: [原始HBC][MAGIC_START][LENGTH][JSON_DATA][MAGIC_END]
26
+ */
27
+ function readMetadataFromHBCFooter(buffer) {
28
+ const MAGIC = Buffer.from('RNUPDATE', 'utf8'); // 8 bytes
29
+ const MAGIC_SIZE = 8;
30
+ const LENGTH_SIZE = 4;
31
+
32
+ console.log(`\n[DEBUG] Reading metadata from HBC footer...`);
33
+ console.log(`[DEBUG] File size: ${buffer.length} bytes`);
34
+ console.log(`[DEBUG] Last 100 bytes (hex): ${buffer.slice(-100).toString('hex')}`);
35
+ console.log(`[DEBUG] Last 50 bytes (utf8): ${buffer.slice(-50).toString('utf8', 0, 50).replace(/[^\x20-\x7E]/g, '.')}`);
36
+
37
+ // 检查文件是否足够大以包含 meta 块
38
+ // 最小大小: MAGIC_START(8) + LENGTH(4) + JSON(至少2字节"{}") + MAGIC_END(8) = 22 bytes
39
+ if (buffer.length < 22) {
40
+ console.log(`[DEBUG] File too small: ${buffer.length} < 22`);
41
+ return null;
42
+ }
43
+
44
+ // 从文件末尾向前查找最后一个 MAGIC_END
45
+ const lastMagicIndex = buffer.lastIndexOf(MAGIC);
46
+
47
+ console.log(`[DEBUG] MAGIC string: "${MAGIC.toString('utf8')}"`);
48
+ console.log(`[DEBUG] Last MAGIC index: ${lastMagicIndex}`);
49
+
50
+ if (lastMagicIndex === -1) {
51
+ console.log(`[DEBUG] MAGIC not found in file`);
52
+ return null;
53
+ }
54
+
55
+ // MAGIC_END 应该在文件末尾
56
+ console.log(`[DEBUG] Expected MAGIC_END at: ${buffer.length - MAGIC_SIZE}`);
57
+ console.log(`[DEBUG] Found MAGIC_END at: ${lastMagicIndex}`);
58
+
59
+ if (lastMagicIndex + MAGIC_SIZE !== buffer.length) {
60
+ console.warn(`⚠️ Found MAGIC but not at file end (expected ${buffer.length - MAGIC_SIZE}, found ${lastMagicIndex})`);
61
+ return null;
62
+ }
63
+
64
+ // 计算 MAGIC_START 的位置
65
+ // 从 MAGIC_END 向前:MAGIC_END(8) + JSON(?) + LENGTH(4) + MAGIC_START(8)
66
+ const magicEndStart = lastMagicIndex;
67
+
68
+ // 读取长度字段(在 MAGIC_END 之前的 4 字节)
69
+ const lengthStart = magicEndStart - LENGTH_SIZE;
70
+ if (lengthStart < MAGIC_SIZE) {
71
+ console.log(`[DEBUG] lengthStart too small: ${lengthStart} < ${MAGIC_SIZE}`);
72
+ return null; // 文件太小
73
+ }
74
+
75
+ const jsonLength = buffer.readUInt32LE(lengthStart);
76
+ console.log(`[DEBUG] JSON length from buffer: ${jsonLength}`);
77
+
78
+ // 验证长度是否合理(JSON 数据应该小于 10KB)
79
+ if (jsonLength > 10240 || jsonLength < 2) {
80
+ console.warn(`⚠️ Invalid JSON length: ${jsonLength}`);
81
+ return null;
82
+ }
83
+
84
+ // 计算 JSON 数据的起始位置
85
+ const jsonStart = lengthStart - jsonLength;
86
+ console.log(`[DEBUG] JSON start position: ${jsonStart}`);
87
+ if (jsonStart < MAGIC_SIZE) {
88
+ console.log(`[DEBUG] jsonStart too small: ${jsonStart} < ${MAGIC_SIZE}`);
89
+ return null;
90
+ }
91
+
92
+ // 验证 MAGIC_START
93
+ const magicStartPos = jsonStart - MAGIC_SIZE;
94
+ console.log(`[DEBUG] MAGIC_START position: ${magicStartPos}`);
95
+ const magicStart = buffer.slice(magicStartPos, jsonStart);
96
+ console.log(`[DEBUG] MAGIC_START bytes: ${magicStart.toString('hex')}`);
97
+ console.log(`[DEBUG] Expected MAGIC bytes: ${MAGIC.toString('hex')}`);
98
+
99
+ if (!magicStart.equals(MAGIC)) {
100
+ console.warn('⚠️ MAGIC_START mismatch');
101
+ console.warn(` Expected: ${MAGIC.toString('hex')}`);
102
+ console.warn(` Got: ${magicStart.toString('hex')}`);
103
+ return null;
104
+ }
105
+
106
+ // 读取 JSON 数据
107
+ try {
108
+ const jsonBuffer = buffer.slice(jsonStart, lengthStart);
109
+ const jsonString = jsonBuffer.toString('utf8');
110
+ console.log(`[DEBUG] JSON string: ${jsonString}`);
111
+ const metadata = JSON.parse(jsonString);
112
+
113
+ console.log(`✅ Found metadata in HBC footer: contentHash=${metadata.contentHash?.slice(0, 16)}...`);
114
+ console.log(` Metadata: ${JSON.stringify(metadata, null, 2)}`);
115
+
116
+ return metadata;
117
+ } catch (error) {
118
+ console.error('❌ Failed to parse metadata JSON:', error);
119
+ return null;
120
+ }
121
+ }
122
+
123
+ /**
124
+ * 从 Hermes -meta 参数注入的数据读取(备用方案)
125
+ */
126
+ function readMetadataFromHermesMeta(buffer) {
127
+ const metadata = {};
128
+ const searchPattern = Buffer.from('contentHash=', 'utf8');
129
+
130
+ // 在整个文件中查找模式
131
+ let index = buffer.indexOf(searchPattern);
132
+
133
+ if (index !== -1) {
134
+ // 找到了 "contentHash=",读取后面的 hash 值
135
+ const hashStart = index + searchPattern.length;
136
+
137
+ // Hash 应该是 64 个十六进制字符 (SHA256)
138
+ let hashEnd = hashStart;
139
+ while (hashEnd < buffer.length && hashEnd < hashStart + 64) {
140
+ const byte = buffer[hashEnd];
141
+ // 检查是否是有效的十六进制字符 (0-9, a-f, A-F)
142
+ if ((byte >= 48 && byte <= 57) || // 0-9
143
+ (byte >= 97 && byte <= 102) || // a-f
144
+ (byte >= 65 && byte <= 70)) { // A-F
145
+ hashEnd++;
146
+ } else {
147
+ break;
148
+ }
149
+ }
150
+
151
+ if (hashEnd > hashStart) {
152
+ metadata.contentHash = buffer.toString('utf8', hashStart, hashEnd);
153
+ console.log(`✅ Found contentHash in Hermes -meta: ${metadata.contentHash.slice(0, 16)}...`);
154
+ }
155
+ } else {
156
+ console.warn('⚠️ contentHash not found in Hermes -meta parameter');
157
+ }
158
+
159
+ return metadata;
160
+ }
161
+
162
+ /**
163
+ * 从普通 JS bundle 的注释中读取 metadata
164
+ */
165
+ function readMetadataFromJSBundle(buffer) {
166
+ const content = buffer.toString('utf8');
167
+ const metadata = {};
168
+
169
+ // 查找 //# BUNDLE_METADATA {...} 注释
170
+ const metaMatch = content.match(/\/\/# BUNDLE_METADATA\s+(\{[^}]+\})/);
171
+ if (metaMatch) {
172
+ try {
173
+ const metaObj = JSON.parse(metaMatch[1]);
174
+ metadata.contentHash = metaObj.contentHash;
175
+ console.log(`✅ Found contentHash in JS bundle comment: ${metadata.contentHash.slice(0, 16)}...`);
176
+ } catch (e) {
177
+ console.error('Failed to parse BUNDLE_METADATA comment:', e);
178
+ }
179
+ } else {
180
+ console.warn('⚠️ BUNDLE_METADATA comment not found in JS bundle');
181
+ }
182
+
183
+ return metadata;
184
+ }
185
+
186
+ // 测试读取
187
+ const metadata = readBundleMetadata('./index.bundlejs');
188
+ console.log('Final metadata:', metadata);
@@ -0,0 +1,103 @@
1
+ import { describe, expect, test, mock } from 'bun:test';
2
+
3
+ // In Bun, top-level imports are cached.
4
+ // We can use mock.module to change the implementation of a module,
5
+ // but if a module has already been executed (like core.ts),
6
+ // re-importing it might not re-run the top-level code unless we use some tricks
7
+ // or run tests in isolation.
8
+ // Actually, bun test runs each file in its own environment usually,
9
+ // BUT if we run multiple test files in one process, they might share the cache.
10
+ const importFreshCore = (cacheKey: string) => import(`../core?${cacheKey}`);
11
+
12
+ describe('core info parsing', () => {
13
+ test('should call error when currentVersionInfo is invalid JSON', async () => {
14
+ const mockError = mock(() => {});
15
+
16
+ mock.module('react-native', () => ({
17
+ Platform: {
18
+ OS: 'ios',
19
+ Version: 13,
20
+ },
21
+ NativeModules: {
22
+ Pushy: {
23
+ currentVersionInfo: '{invalid}',
24
+ downloadRootDir: '/tmp',
25
+ packageVersion: '1.0.0',
26
+ currentVersion: 'hash1',
27
+ isFirstTime: false,
28
+ rolledBackVersion: '',
29
+ buildTime: '2023-01-01',
30
+ uuid: 'existing-uuid',
31
+ setLocalHashInfo: mock(() => {}),
32
+ getLocalHashInfo: mock(() => Promise.resolve('{}')),
33
+ setUuid: mock(() => {}),
34
+ },
35
+ },
36
+ NativeEventEmitter: class {
37
+ addListener = mock(() => ({ remove: mock(() => {}) }));
38
+ },
39
+ }));
40
+
41
+ mock.module('react-native/Libraries/Core/ReactNativeVersion', () => ({
42
+ version: { major: 0, minor: 73, patch: 0 },
43
+ }));
44
+
45
+ mock.module('nanoid/non-secure', () => ({
46
+ nanoid: () => 'mock-uuid',
47
+ }));
48
+
49
+ mock.module('../utils', () => ({
50
+ error: mockError,
51
+ log: mock(() => {}),
52
+ emptyModule: {},
53
+ }));
54
+
55
+ // Use a unique query parameter to bypass cache if supported, or just rely on fresh environment per file.
56
+ // In Bun, you can sometimes use a cache buster if it's dynamic import.
57
+ await importFreshCore('error');
58
+
59
+ expect(mockError).toHaveBeenCalledWith(
60
+ expect.stringContaining('error_parse_version_info')
61
+ );
62
+ });
63
+
64
+ test('should not call error when currentVersionInfo is valid JSON', async () => {
65
+ const mockError = mock(() => {});
66
+ const mockSetLocalHashInfo = mock(() => {});
67
+
68
+ mock.module('react-native', () => ({
69
+ Platform: {
70
+ OS: 'ios',
71
+ Version: 13,
72
+ },
73
+ NativeModules: {
74
+ Pushy: {
75
+ currentVersionInfo: JSON.stringify({ name: 'v1', debugChannel: true }),
76
+ downloadRootDir: '/tmp',
77
+ packageVersion: '1.0.0',
78
+ currentVersion: 'hash1',
79
+ isFirstTime: false,
80
+ rolledBackVersion: '',
81
+ buildTime: '2023-01-01',
82
+ uuid: 'existing-uuid',
83
+ setLocalHashInfo: mockSetLocalHashInfo,
84
+ getLocalHashInfo: mock(() => Promise.resolve('{}')),
85
+ setUuid: mock(() => {}),
86
+ },
87
+ },
88
+ NativeEventEmitter: class {
89
+ addListener = mock(() => ({ remove: mock(() => {}) }));
90
+ },
91
+ }));
92
+
93
+ mock.module('../utils', () => ({
94
+ error: mockError,
95
+ log: mock(() => {}),
96
+ emptyModule: {},
97
+ }));
98
+
99
+ await importFreshCore('success');
100
+
101
+ expect(mockError).not.toHaveBeenCalled();
102
+ });
103
+ });
@@ -0,0 +1,37 @@
1
+ import { mock } from 'bun:test';
2
+
3
+ mock.module('react-native', () => {
4
+ return {
5
+ Platform: {
6
+ OS: 'ios',
7
+ Version: 13,
8
+ },
9
+ NativeModules: {
10
+ Pushy: {
11
+ currentVersionInfo: '{}',
12
+ downloadRootDir: '/tmp',
13
+ packageVersion: '1.0.0',
14
+ currentVersion: 'hash',
15
+ isFirstTime: false,
16
+ rolledBackVersion: '',
17
+ buildTime: '2023-01-01',
18
+ uuid: 'uuid',
19
+ setLocalHashInfo: () => {},
20
+ getLocalHashInfo: () => Promise.resolve('{}'),
21
+ setUuid: () => {},
22
+ },
23
+ },
24
+ NativeEventEmitter: class {
25
+ addListener = () => ({ remove: () => {} });
26
+ removeAllListeners = () => {};
27
+ },
28
+ };
29
+ });
30
+
31
+ mock.module('../i18n', () => {
32
+ return {
33
+ default: {
34
+ t: (key: string, params?: any) => `${key}${params ? JSON.stringify(params) : ''}`,
35
+ },
36
+ };
37
+ });
@@ -0,0 +1,36 @@
1
+ import { describe, expect, test, mock } from 'bun:test';
2
+
3
+ mock.module('react-native', () => {
4
+ return {
5
+ Platform: {
6
+ OS: 'ios',
7
+ },
8
+ };
9
+ });
10
+
11
+ mock.module('../i18n', () => {
12
+ return {
13
+ default: {
14
+ t: (key: string) => key,
15
+ },
16
+ };
17
+ });
18
+
19
+ import { joinUrls } from '../utils';
20
+
21
+ describe('joinUrls', () => {
22
+ test('returns undefined when fileName is not provided', () => {
23
+ expect(joinUrls(['example.com'])).toBeUndefined();
24
+ });
25
+
26
+ test('returns an empty array when paths is empty', () => {
27
+ expect(joinUrls([], 'file.txt')).toEqual([]);
28
+ });
29
+
30
+ test('maps over paths and prepends https:// with fileName', () => {
31
+ expect(joinUrls(['example.com', 'test.org'], 'file.txt')).toEqual([
32
+ 'https://example.com/file.txt',
33
+ 'https://test.org/file.txt',
34
+ ]);
35
+ });
36
+ });
package/src/client.ts CHANGED
@@ -22,6 +22,7 @@ import {
22
22
  assertWeb,
23
23
  emptyObj,
24
24
  enhancedFetch,
25
+ info,
25
26
  joinUrls,
26
27
  log,
27
28
  noop,
@@ -189,7 +190,7 @@ export class Pushy {
189
190
  };
190
191
  assertDebug = (matter: string) => {
191
192
  if (__DEV__ && !this.options.debug) {
192
- console.info(this.t('dev_debug_disabled', { matter }));
193
+ info(this.t('dev_debug_disabled', { matter }));
193
194
  return false;
194
195
  }
195
196
  return true;
@@ -323,9 +324,10 @@ export class Pushy {
323
324
  this.throwIfEnabled(Error('errorChecking: ' + errorMessage));
324
325
  return this.lastRespJson ? await this.lastRespJson : emptyObj;
325
326
  }
326
- this.lastRespJson = resp.json();
327
+ const respJsonPromise = resp.json() as Promise<CheckResult>;
328
+ this.lastRespJson = respJsonPromise;
327
329
 
328
- const result: CheckResult = await this.lastRespJson;
330
+ const result: CheckResult = await respJsonPromise;
329
331
 
330
332
  log('checking result:', result);
331
333
 
@@ -344,9 +346,14 @@ export class Pushy {
344
346
  const remoteEndpoints = await resp.json();
345
347
  log('fetch endpoints:', remoteEndpoints);
346
348
  if (Array.isArray(remoteEndpoints)) {
347
- server.backups = Array.from(
348
- new Set([...(server.backups || []), ...remoteEndpoints]),
349
- );
349
+ const backups = server.backups || [];
350
+ const set = new Set(backups);
351
+ for (const endpoint of remoteEndpoints) {
352
+ set.add(endpoint);
353
+ }
354
+ if (set.size !== backups.length) {
355
+ server.backups = Array.from(set);
356
+ }
350
357
  }
351
358
  } catch (e: any) {
352
359
  log('failed to fetch endpoints from: ', server.queryUrls);
@@ -355,7 +362,7 @@ export class Pushy {
355
362
  return server.backups;
356
363
  };
357
364
  downloadUpdate = async (
358
- info: CheckResult,
365
+ updateInfo: CheckResult,
359
366
  onDownloadProgress?: (data: ProgressData) => void,
360
367
  ) => {
361
368
  const {
@@ -367,15 +374,15 @@ export class Pushy {
367
374
  name,
368
375
  description = '',
369
376
  metaInfo,
370
- } = info;
377
+ } = updateInfo;
371
378
  if (
372
379
  this.options.beforeDownloadUpdate &&
373
- (await this.options.beforeDownloadUpdate(info)) === false
380
+ (await this.options.beforeDownloadUpdate(updateInfo)) === false
374
381
  ) {
375
382
  log('beforeDownloadUpdate returned false, skipping download');
376
383
  return;
377
384
  }
378
- if (!info.update || !hash) {
385
+ if (!updateInfo.update || !hash) {
379
386
  return;
380
387
  }
381
388
  if (rolledBackVersion === hash) {
package/src/core.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
2
- import { emptyModule, log } from './utils';
2
+ import { emptyModule, error, log } from './utils';
3
+ import i18n from './i18n';
3
4
  const {
4
5
  version: v,
5
6
  } = require('react-native/Libraries/Core/ReactNativeVersion');
@@ -46,10 +47,9 @@ if (currentVersionInfoString) {
46
47
  delete _currentVersionInfo.debugChannel;
47
48
  setLocalHashInfo(currentVersion, _currentVersionInfo);
48
49
  }
49
- } catch (error) {
50
- console.error(
51
- 'Failed to parse currentVersionInfo:',
52
- currentVersionInfoString,
50
+ } catch (err) {
51
+ error(
52
+ i18n.t('error_parse_version_info', { info: currentVersionInfoString }),
53
53
  );
54
54
  }
55
55
  }
package/src/locales/en.ts CHANGED
@@ -44,6 +44,7 @@ export default {
44
44
 
45
45
  // Error messages
46
46
  error_appkey_required: 'appKey is required',
47
+ error_parse_version_info: 'Failed to parse currentVersionInfo: {{info}}',
47
48
  error_update_check_failed: 'Update check failed',
48
49
  error_cannot_connect_server:
49
50
  'Can not connect to update server. Please check your network.',
package/src/locales/zh.ts CHANGED
@@ -42,6 +42,7 @@ export default {
42
42
 
43
43
  // Error messages
44
44
  error_appkey_required: '需要提供 appKey',
45
+ error_parse_version_info: '解析 currentVersionInfo 失败: {{info}}',
45
46
  error_update_check_failed: '更新检查失败',
46
47
  error_cannot_connect_server: '无法连接到更新服务器。请检查网络连接。',
47
48
  error_cannot_connect_backup:
package/src/utils.ts CHANGED
@@ -5,6 +5,18 @@ export function log(...args: any[]) {
5
5
  console.log(i18n.t('dev_log_prefix'), ...args);
6
6
  }
7
7
 
8
+ export function info(...args: any[]) {
9
+ console.info(i18n.t('dev_log_prefix'), ...args);
10
+ }
11
+
12
+ export function warn(...args: any[]) {
13
+ console.warn(i18n.t('dev_log_prefix'), ...args);
14
+ }
15
+
16
+ export function error(...args: any[]) {
17
+ console.error(i18n.t('dev_log_prefix'), ...args);
18
+ }
19
+
8
20
  export const isWeb = Platform.OS === 'web';
9
21
 
10
22
  export function promiseAny<T>(promises: Promise<T>[]) {
@@ -93,7 +105,7 @@ export const testUrls = async (urls?: string[]) => {
93
105
 
94
106
  export const assertWeb = () => {
95
107
  if (isWeb) {
96
- console.warn(i18n.t('dev_web_not_supported'));
108
+ warn(i18n.t('dev_web_not_supported'));
97
109
  return false;
98
110
  }
99
111
  return true;